Day-to-day operational procedures for StellarWork in production: monitoring, backups, incident response, and maintenance tasks.
For initial deployment steps see DEPLOYMENT.md. For incident severity and escalation paths see production-escalation.md.
- Monitoring
- Backup Procedures
- Incident Response
- Emergency Contract Upgrade
- Fund Recovery Procedures
- Regular Maintenance Tasks
The StellarWork platform has three observable layers: the Soroban RPC, on-chain contract state, and the Next.js frontend.
| Signal | Healthy | Degraded | Critical |
|---|---|---|---|
| RPC response time | < 2 s | 2–10 s | > 10 s or no response |
getTransaction success rate |
> 99% | 90–99% | < 90% |
simulateTransaction errors |
None | Occasional | Repeated FAILED |
| Ledger close time | ~5 s | 10–30 s | > 30 s or stalled |
Quick RPC health check:
curl -s https://soroban-rpc.stellar.org \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getNetwork","params":{}}' \
| jq '.result.passphrase'
# Should print: "Public Global Stellar Network ; September 2015"Check the latest ledger sequence:
curl -s https://soroban-rpc.stellar.org \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"getLatestLedger","params":{}}' \
| jq '.result'| Signal | Check | Command |
|---|---|---|
| Job counter | Incrementing as expected | get_job_count |
| Fee accrual | Growing with completed jobs | get_fees <token> |
| Admin address | Has not changed unexpectedly | get_admin |
| Contract version | Matches last deployed version | get_contract_version |
Routine state check script:
#!/bin/bash
# ops-check.sh — run daily
CONTRACT_ID="<YOUR_CONTRACT_ID>"
NETWORK="mainnet"
NATIVE_TOKEN="CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
echo "=== StellarWork Contract Health ==="
echo "Job count:"
soroban contract invoke --id $CONTRACT_ID --network $NETWORK -- get_job_count
echo "Accrued fees (stroops):"
soroban contract invoke --id $CONTRACT_ID --network $NETWORK \
-- get_fees --token $NATIVE_TOKEN
echo "Admin address:"
soroban contract invoke --id $CONTRACT_ID --network $NETWORK -- get_admin
echo "Contract version:"
soroban contract invoke --id $CONTRACT_ID --network $NETWORK -- get_contract_version| Signal | Check | Tool |
|---|---|---|
| Page load success | HTTP 200 on / |
curl -s -o /dev/null -w "%{http_code}" https://stellarwork.app |
| Deployment status | Latest deployment shows Ready | Vercel Dashboard |
| JS bundle errors | No console errors on page load | Browser DevTools |
| Wallet connect flow | Freighter popup appears | Manual smoke test |
Minimal uptime check:
# Returns 200 for OK, anything else warrants investigation
curl -s -o /dev/null -w "%{http_code}\n" https://stellarwork.app- Stellar network status page: https://status.stellar.org
- Subscribe to status alerts for incidents affecting the public network or Soroban.
- Planned network upgrades (Protocol Votes) are announced on the Stellar Blog.
Recommended minimum alerting stack:
| What | Tool | Trigger |
|---|---|---|
| Frontend uptime | Uptime Robot (free) or Better Uptime | HTTP non-200 for 2+ minutes |
| Vercel deployment failure | Vercel email notifications | Deployment status = Error |
| Stellar network incident | status.stellar.org RSS → Slack/email | Any new incident |
| RPC errors in browser | Sentry (or similar) in Next.js | Uncaught exceptions / API errors |
To add Sentry to the Next.js frontend:
cd frontend
npm install @sentry/nextjs
npx @sentry/wizard@latest -i nextjsFreighter transaction submissions are visible in the Stellar Explorer. For bulk monitoring:
# Get recent contract events (requires Horizon or RPC event subscription)
soroban contract events \
--id $CONTRACT_ID \
--network mainnet \
--start-ledger <LEDGER_NUMBER>Events to watch for unexpected spikes:
job_disputed— elevated dispute rate may indicate UX confusion or bad actorsdeadline_enforced— elevated rate may indicate unrealistic deadlinesupgrade_proposed— should only appear when you initiate an upgrade
The escrow contract stores all financial state on-chain. Funds are never at risk from a server failure. However, certain operational data must be backed up to maintain admin control.
| Item | Where it lives | Backup method | Frequency |
|---|---|---|---|
| Admin secret key | Hardware wallet or secrets manager | Offline, encrypted, geographically distributed | Once at creation; verify quarterly |
| Contract ID | Vercel env vars, deployment log | At least 3 independent locations | At deployment; verify monthly |
| WASM artifact + hash | Build output | S3/GCS versioned bucket or Git tag | Every contract release |
| Admin address (public key) | Vercel env vars, deployment log | At least 3 locations | At deployment |
| Token allowlist | On-chain (queryable) | Document in deployment log | When changed |
| Fee tier configuration | On-chain (queryable) | Document in deployment log | When changed |
The admin key controls:
- Fee withdrawal (
withdraw_fees) - Dispute resolution (
resolve_dispute) - Contract upgrades (
propose_upgrade,execute_upgrade) - Token management (
add_allowed_token,remove_allowed_token) - Admin transfer (
transfer_admin)
Backup procedure:
- Generate the admin keypair offline (air-gapped machine if possible).
- Write the secret key on paper (or metal) — do not save it in digital-only form.
- Store copies in at least two physically separate, secure locations (e.g. office safe + home safe).
- Verify the backup by importing the secret key into a test CLI profile and confirming the public key matches.
- Record the date of backup and the verifying team member in the deployment log.
# Verify backup: import and check public key
soroban config identity add backup-verify --secret-key <SECRET_FROM_BACKUP>
soroban config identity address backup-verify
# Must match: $ADMIN_ADDRESS
soroban config identity rm backup-verify # clean upTo snapshot all job state for offline analysis or disaster recovery:
#!/bin/bash
# snapshot-jobs.sh
CONTRACT_ID="<YOUR_CONTRACT_ID>"
NETWORK="mainnet"
OUTFILE="snapshot-$(date +%Y%m%d-%H%M%S).jsonl"
COUNT=$(soroban contract invoke --id $CONTRACT_ID --network $NETWORK -- get_job_count)
echo "Snapshotting $COUNT jobs..."
for i in $(seq 1 $COUNT); do
soroban contract invoke \
--id $CONTRACT_ID \
--network $NETWORK \
-- get_job --job_id $i >> $OUTFILE
done
echo "Snapshot written to $OUTFILE"Run this before any planned maintenance or upgrade. Store snapshots in a versioned object storage bucket.
Record the current contract configuration in a text file and commit it to a private ops repository:
#!/bin/bash
# backup-config.sh
echo "Contract ID: $CONTRACT_ID"
echo "Admin: $(soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_admin)"
echo "Fee BPS: $(soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_fee_bps)"
echo "Version: $(soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_contract_version)"
echo "Job count: $(soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_job_count)"
echo "Snapshot date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"Follow the severity matrix in production-escalation.md to determine priority. Use the procedures below for common scenarios.
Symptoms: Users report the site is down; uptime monitor fires; HTTP non-200.
Steps:
- Check Vercel Dashboard → Deployments. Is the latest deployment Ready?
- If the latest deployment has errors, roll back immediately:
Vercel Dashboard → Deployments → [previous deployment] → Promote to Production - If all deployments show errors, check Vercel status: https://www.vercel-status.com
- If Vercel is healthy, check the build log for the failing deployment. Common causes:
- Missing
NEXT_PUBLIC_CONTRACT_IDenv var → add it and redeploy - TypeScript compile error from a recent merge → revert the PR and redeploy
- Missing
- Verify recovery:
curl -s -o /dev/null -w "%{http_code}\n" https://stellarwork.app # Should return 200
Symptoms: Users report "Transaction failed" errors; simulateTransaction returns errors.
Steps:
- Check Stellar network status: https://status.stellar.org
- If a network-wide incident is active, no action needed — wait for resolution.
- If the network is healthy, test the RPC endpoint directly:
curl -s https://soroban-rpc.stellar.org \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"getLatestLedger","params":{}}' \ | jq '.result.sequence'
- If the public RPC is degraded, update
NEXT_PUBLIC_SOROBAN_RPCin Vercel to point to a backup provider and redeploy. - Monitor: watch for error resolution in Sentry or browser reports.
Symptoms: Admin address changed unexpectedly; fees withdrawn without authorisation; dispute resolved with wrong split.
Steps:
- Immediately verify current admin address:
soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_admin - Compare with the expected admin address in your deployment log.
- If the admin has changed, the admin key is compromised.
- If you still control the admin key: transfer admin to a fresh keypair immediately:
NEW_ADMIN=$(soroban config identity generate emergency-admin && \ soroban config identity address emergency-admin) soroban contract invoke \ --id $CONTRACT_ID \ --source stellarwork-prod \ --network mainnet \ -- transfer_admin \ --caller $ADMIN_ADDRESS \ --new_admin $NEW_ADMIN
- Update
NEXT_PUBLIC_ADMIN_ADDRESSin Vercel env vars and redeploy. - Revoke/rotate all secrets in your secrets manager.
- File a private security report per SECURITY.md.
Symptoms: resolve_dispute panics with InvalidStatus (#3) — freelancer is null.
Steps:
- Retrieve the job state:
soroban contract invoke \ --id $CONTRACT_ID \ --network mainnet \ -- get_job --job_id <JOB_ID>
- If
freelanceris null, the job was never accepted — it cannot be inDisputedstatus under normal contract logic. This indicates data corruption or an unexpected state. - Escalate to SEV-1 per production-escalation.md.
- Take a full job state snapshot (see Section 2.3).
- Do not attempt further contract operations until the root cause is understood.
The escrow contract supports a 24-hour timelock upgrade mechanism. Use this when a critical bug must be patched in production.
Warning: Contract upgrades replace the WASM bytecode while preserving storage. All existing job state remains intact. Test the new WASM thoroughly before proposing an upgrade on mainnet.
Step 1 — Build and test the fixed WASM:
cd contracts/escrow
cargo test
soroban contract build
sha256sum target/wasm32-unknown-unknown/release/escrow.wasm
# Save the hashStep 2 — Upload the WASM to the network:
soroban contract upload \
--wasm target/wasm32-unknown-unknown/release/escrow.wasm \
--source stellarwork-prod \
--network mainnet
# Returns a 32-byte WASM hash (hex)Step 3 — Propose the upgrade (starts 24-hour timelock):
soroban contract invoke \
--id $CONTRACT_ID \
--source stellarwork-prod \
--network mainnet \
-- propose_upgrade \
--admin $ADMIN_ADDRESS \
--new_wasm_hash <WASM_HASH_FROM_STEP_2>The contract stores the pending upgrade WASM hash and a deadline (current timestamp + 86,400 seconds). The upgrade_proposed event is emitted.
Step 4 — Wait 24 hours.
This window allows users to review the proposed upgrade on-chain via the PendingUpgradeWasmHash storage key or event log.
Step 5 — Execute the upgrade (after timelock expires):
soroban contract invoke \
--id $CONTRACT_ID \
--source stellarwork-prod \
--network mainnet \
-- execute_upgrade \
--admin $ADMIN_ADDRESSThe contract_upgraded event is emitted. Verify the upgrade:
soroban contract invoke \
--id $CONTRACT_ID \
--network mainnet \
-- get_contract_version
# Should return the new version numberIf you need to cancel a pending upgrade (e.g., the proposed WASM was found to be incorrect):
soroban contract invoke \
--id $CONTRACT_ID \
--source stellarwork-prod \
--network mainnet \
-- cancel_upgrade \
--admin $ADMIN_ADDRESSThe upgrade_cancelled event is emitted. No new upgrade can be proposed until the cancellation is confirmed.
The current contract enforces the 24-hour timelock strictly — execute_upgrade will panic with UpgradeTimelockPending (#19) if called before the deadline. There is no admin bypass.
If a critical vulnerability requires immediate mitigation before the timelock expires:
- Remove the frontend by deleting
NEXT_PUBLIC_CONTRACT_IDfrom Vercel env vars and redeploying. This prevents users from interacting with the contract through the UI. - Post a public notice explaining the maintenance window using maintenance-window-announcement-template.md.
- Wait for the timelock and execute the upgrade.
- Restore the frontend env vars and redeploy once the upgrade is confirmed.
Funds held in escrow are secure as long as the contract logic is correct and the admin key is not compromised. Use these procedures for edge cases.
If a dispute was raised and the admin cannot resolve it (e.g., admin key lost):
- Transfer the admin role to a recovery address (if you still hold the original admin key):
soroban contract invoke \ --id $CONTRACT_ID \ --source stellarwork-prod \ --network mainnet \ -- transfer_admin \ --caller $ADMIN_ADDRESS \ --new_admin <RECOVERY_ADDRESS>
- Use the recovery admin to call
resolve_disputewith an appropriate split.
If the admin key is permanently lost and funds are stuck, this is a critical security incident. File a report per SECURITY.md and engage Stellar development support.
Soroban storage entries have a time-to-live (TTL). Active jobs have their TTL bumped automatically on every write operation. For jobs that have been open for an unusually long time without activity, call extend_job_ttl to prevent archival:
soroban contract invoke \
--id $CONTRACT_ID \
--source stellarwork-prod \
--network mainnet \
-- extend_job_ttl \
--caller $ADMIN_ADDRESS \
--job_id <JOB_ID>Check for at-risk jobs (those in non-terminal states for > 60 days) as part of monthly maintenance.
If a freelancer stops responding and the job has a deadline:
- Wait for the deadline to pass.
- The client can call
enforce_deadlineto cancel the job and receive a full refund:soroban contract invoke \ --id $CONTRACT_ID \ --source <CLIENT_SECRET_OR_IDENTITY> \ --network mainnet \ -- enforce_deadline \ --client <CLIENT_ADDRESS> \ --job_id <JOB_ID>
- If no deadline was set and the freelancer is unreachable, the client can raise a dispute via the admin resolution path.
- Check Vercel deployment status — confirm production deployment is Ready.
- Review Sentry (or equivalent) for new uncaught errors in the frontend.
- Check Stellar network status for upcoming planned maintenance.
- Review open GitHub issues labelled
bugorpriority: high.
| Task | Command / Action |
|---|---|
| Check accrued fees | soroban contract invoke --id $CONTRACT_ID --network mainnet -- get_fees --token <TOKEN> |
| Withdraw fees if above threshold | Admin panel → Withdraw Fees (or via CLI withdraw_fees) |
| Verify admin address on-chain | soroban contract invoke -- get_admin → compare with records |
| Check for long-running open jobs | Review job snapshots for jobs open > 60 days; consider extend_job_ttl |
| Dependency audit | cd frontend && npm audit — resolve critical/high severity issues |
| Soroban CLI update | cargo install --locked soroban-cli |
| Review active disputes | Admin panel → Disputes — resolve any stuck in Active or UnderReview |
| Task | Action |
|---|---|
| Admin key rotation | Transfer admin to a freshly generated keypair. Update NEXT_PUBLIC_ADMIN_ADDRESS in Vercel. |
| Backup verification | Import admin key from backup and verify public key matches current admin. |
| Dependency update | cd frontend && npm update — review changelog for breaking changes before committing. |
| Security audit | Review contract for known Soroban vulnerability patterns. Check Stellar security advisories. |
| Docs review | Verify DEPLOYMENT.md, environments.md, and this runbook are accurate. |
| Contract TTL check | Ensure instance storage TTL is not at risk of expiry. Call any admin function to bump it if needed. |
Fees accrue in the contract as jobs are completed. Withdraw them periodically:
-
Check accrued fees:
soroban contract invoke \ --id $CONTRACT_ID \ --network mainnet \ -- get_fees \ --token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC -
If fees > 0, withdraw via the Admin panel or CLI:
soroban contract invoke \ --id $CONTRACT_ID \ --source stellarwork-prod \ --network mainnet \ -- withdraw_fees \ --token CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC -
Confirm admin wallet received the expected amount via Stellar Explorer.
-
Record the withdrawal in the ops log (amount, date, transaction hash).
cd frontend
# Check for outdated packages
npm outdated
# Run security audit
npm audit
# Update dependencies (review changelog before merging)
npm update
# For major version bumps, update manually and test
npm install @stellar/stellar-sdk@latest
npm install @stellar/freighter-api@latest
# Run tests after updating
npm run typecheck
npm run lint
npm run buildFor Rust dependencies:
cd contracts/escrow
cargo update
cargo testRelated: DEPLOYMENT.md · PRODUCTION_CHECKLIST.md · production-escalation.md · release-checklist.md