15 KiB
Morphit — automation audit of manual interventions (Q6)
Last reviewed: 2026-05-01
This catalog enumerates every place in Morphit where a human operator, admin, or user has to do something manually that could potentially be automated. For each item: the current state, the automation potential (HIGH / MEDIUM / LOW), the cost, and the recommendation.
The goal isn't to automate everything — some manual gates exist deliberately to preserve operator control over expensive, irreversible, or high-trust actions. The goal is to surface where automation would meaningfully reduce operator load without increasing risk.
This audit complements docs/OPERATIONS.md (the operator
runbook) and docs/RUN-A-MORPHIT-NODE.md (the new-operator
walkthrough). Both must stay aligned with this catalog.
1. Operator-side manual tasks
1.1 Weekly ACT minting ceremony — ❌ REMOVED (beta.28 / cp329)
There is no ACT minting task any more, so there is nothing to
automate here. Blurt disabled claim_account /
create_claimed_account at hard fork 2, so Account Creation
Tokens cannot be minted at all. As of beta.28 (ADR-0010, as
amended 2026-06) the relay creates each account with a direct
account_create op, paying the ~100 BLURT account_creation_fee
inline from its liquid BLURT at signup time. The
mint-acts.ts script, the morphit-relay-mint-acts.{service,timer}
units, and the MORPHIT_RELAY_WEEKLY_ACT_COUNT /
MORPHIT_RELAY_AUTOMINT_* config knobs have all been removed.
Operator-side, the only standing task is funding — keep
@morphit-relay topped up with enough liquid BLURT to cover
signups (see OPERATIONS §0a for the sizing math and §16 for the
relay_low_balance_for_signups Matrix alert). That funding is
itself "automatable" via a recurrent on-chain transfer (§1.2),
and the low-balance alerting is already automated (§1.6), so no
new automation is owed here.
STATUS: ✅ Implemented 2026-05-01. See OPERATIONS.md §2 "Unattended mode" for the operator-facing setup steps.
1.1-archive: original framing (preserved for ADR-0010 traceability)
Why manual originally: ADR-0010 §4 framed this as a deliberate operator-in-the-loop step so the relay's working BLURT balance stays small and a compromised relay can't drain unbounded BLURT into ACTs. The 2026-05 review concluded the caps still hold under timer-driven minting (§1.1 above), so the in-the-loop confirmation became a cron job.
1.2 Recurrent BLURT top-up to relay — ALREADY AUTOMATED
Today: ADR-0010 §3 ships a recurrent_transfer op the
operator broadcasts ONCE at setup time. Blurt natively
recurses the transfer weekly for executions=52 (one year).
Why this is good: Native chain primitive. No off-chain cron, no hot key on a server. The operator re-broadcasts once a year (or sooner if their fee-account address changes) — that's a quarterly-or-better cadence, not weekly.
STATUS: Already automated; no work to do.
1.3 TLS certificate renewal — ALREADY AUTOMATED
Today: certbot installs a systemd timer that runs twice daily and auto-renews when the cert is within 30 days of expiry.
Recommended addition: a weekly cron.weekly job that
checks expiry and pages the operator if certs ARE near
expiry but renewals haven't fired (catches certbot
configuration drift). This is in OPERATIONS.md §16 (the
morphit-certbot-monitor renewal-stall detector) already.
STATUS: Already automated; the optional alert wrapper is documented but not shipped as a default unit.
1.4 Witness fee change response — MEDIUM automation potential
Today: When Blurt witnesses change account_creation_fee,
the operator sees an alert and manually updates
MORPHIT_INDEXER_ACCOUNT_CREATION_FEE_BLURT and
MORPHIT_RELAY_ACCOUNT_CREATION_FEE_BLURT env vars,
restarts services.
Why manual today: Setting a wrong fallback value silently breaks signup. Operator-in-the-loop catches misreads.
Why automation is constrained: The fallback is exactly that — a fallback for when chain RPCs are unreachable. Auto-updating it from the chain would create a self-referential bootstrap problem if the auto-update path itself relies on chain RPCs.
Recommended partial automation:
- Indexer + relay should both observe
account_creation_feechanges via their normal block-walk and EMIT a structured log line at WARN level when the observed fee differs from the configured fallback by more than 10%. This is already partly there for the indexer (chain-fee tracker); not yet for the relay. - Operator gets an alert via their journald-watching pipeline when the warning fires; manual restart is fast.
Cost: ~20 lines (relay-side warn-log of fee divergence).
STATUS: ✅ Both shipped. Indexer-side tracker existed at
audit time; relay-side warn-log shipped in Part 117 —
apps/relay/src/blurt/client.ts:157 defines
FEE_DIVERGENCE_WARN_THRESHOLD = 0.1 and
analyzeFeeDivergence() at line 168 emits a structured
chain_props_account_creation_fee_diverges_from_config
warn-log when the observed fee differs from the configured
fallback by more than 10%, throttled once-per-process-startup.
Smoke regression at apps/relay/scripts/fee-divergence-smoke.ts
registered in scripts/run-smokes.sh.
1.5 Stale price feed alert — ALREADY AUTOMATED
Today: Indexer's price source emits stale=true after
N minutes without a successful upstream fetch. The frontend
hides the USD echo when stale. Operator sees journald
price.fetch.failed lines.
STATUS: Already automated; alerting is via the operator's normal journald pipeline.
1.6 Operator-account balance monitoring — ALREADY AUTOMATED
Today: Indexer scans operator-fees balances and emits structured alerts when below operator-configured thresholds. Documented at OPERATIONS.md §16.
STATUS: Already automated.
1.7 Relay queue stuck — MEDIUM automation potential
Today: A signup queue entry can get stuck (network errors,
RC depletion, etc.). The operator manually nulls the
broadcast_at to retry.
Why manual today: Stuck entries can mask deeper relay problems (RC depletion, key issues, broken endpoints). An automated retry loop could mask these.
Recommended partial automation:
- Auto-retry up to 3 times with exponential backoff (1m, 5m, 15m). After 3 failures, the entry stays stuck and waits for operator review.
- Today the relay does retry on transient network errors but not on the stuck-entry flagged-for-manual case.
Cost: ~80 lines (queue-entry retry counter + scheduler).
STATUS: Documented as a future hardening pass. Not urgent — operator manual flow is rare and well-documented.
1.8 Schema migrations — ALREADY AUTOMATED
Today: Indexer auto-runs migrations on boot via
apps/indexer/src/db/migrations.ts. v17 has a brief
sequential-scan window during the upgrade documented at
OPERATIONS.md §21; otherwise transparent to the operator.
STATUS: Already automated.
1.9 Server OS package updates — DOCUMENTED + RECOMMENDED
Today: OPERATIONS.md §37.2 recommends unattended-upgrades
for security patches. Operator chooses to enable.
STATUS: Documented; not enforced by Morphit's installer because the installer doesn't exist as a single thing — the operator runs through RUN-A-MORPHIT-NODE.md. The walkthrough SHOULD include enabling unattended-upgrades as a checklist item. (See §3.3 below.)
2. User-side manual tasks (Q9 setup-checklist territory)
These are the user/operator's checklist responsibilities during account setup or daily use. Q9's "operator setup checklist + nudges" answer expands on this — see that section.
2.1 Backup the 12-word seed — ✅ AUTOMATED 2026-05-01
Today: Signup flow displays the seed once with a "I've saved it" gate. Plus, after 7 days of active use on this device, a soft amber banner appears at the top of every page asking "have you backed up your seed somewhere durable?" with a "Show me how →" link to the recovery FAQ and a one-tap permanent dismissal.
Implementation:
- localStorage anchor
morphit.keystore.first_persist_atstamped on first successfulwriteEnvelope(inapps/web/src/lib/crypto/persistentKeystore.ts). - Banner component
SeedBackupNudge.sveltecallsshouldShowSeedBackupNudge()on mount; renders only when ≥7 days since first persist AND not dismissed. - "Got it" sets
morphit.keystore.backup_nudge_dismissed = '1', hiding the banner permanently on this device. - Full sign-out (
clearKeystore) wipes both anchors so re-onboarding restarts the prompt schedule.
STATUS: Shipped.
2.2 Verify counterparty's chat-pub on first contact — ✅ SHIPPED
Today: apps/web/src/lib/components/VerifyPeerPanel.svelte
renders an explicit Verify-peer panel inside ConversationView
showing the deterministic (my_chat_pub, peer_chat_pub)
fingerprint pair (computed in apps/web/src/lib/chat/fingerprint.ts).
Users compare fingerprints out-of-band (voice, in-person,
side-channel) and confirm. That's a manual user action by
design — the machine can't out-of-band compare.
STATUS: Shipped (panel + fingerprint helper + locale strings in all 10 locales).
2.3 Mark trade complete + leave feedback — MANUAL BY DESIGN
Today: After receiving funds, the buyer/seller manually clicks "Trade complete" and "Leave feedback". This is deliberately manual — Morphit doesn't run any wallet, so "funds received" is something only the user can attest to.
STATUS: Cannot be automated without breaking the non-custodial design. Already as automated as it can be.
3. Operator setup tasks (Q9 territory — feeds into the checklist)
3.1 Enable unattended-upgrades — should be in setup checklist
Cost: 1 command in the walkthrough.
STATUS: Documented in OPERATIONS.md §37.2. Should be
flagged in ops-cli init as a checkbox + ufw / fail2ban /
SSH key-only mode.
3.2 Configure systemd timers for auto-tasks — should be in setup checklist
The weekly ACT-mint timer referenced here historically was removed at beta.28 (see §1.1). The remaining auto-task timers the setup wizard should install + enable alongside the relay are the monitoring/backup units (TLS renewal, balance alerts, backups) — none of them touch account creation, which now pays its fee inline per signup.
3.3 ops-cli should verify everything's in place — Q9 deliverable
The ops-cli init wizard already does: chain RPC reachability,
account existence check, key-file perms check, fee-address
viewkey verification, etc. It should ALSO run a final
checklist verification step that pings each of these and
prints "✅ done" or "⚠ missing — run X to fix":
| Check | Today | Recommended |
|---|---|---|
morphit-fees account exists |
✅ done | Keep |
| Active-key file mode 0400 | ✅ done | Keep |
| Indexer DB connection works | ✅ done | Keep |
| RPC endpoint reachability | ✅ done | Keep |
| Relay BLURT balance > floor | ✅ done | Keep |
| TLS cert valid + auto-renew configured | ✅ done | Keep |
unattended-upgrades installed + enabled |
❌ not yet | Add |
ufw enabled + correct ruleset |
❌ not yet | Add |
SSH PasswordAuthentication no |
❌ not yet | Add |
fail2ban installed + sshd jail enabled |
❌ not yet | Add |
| journald disk cap configured | ❌ not yet | Add |
| weekly-mint timer installed (§1.1 shipped 2026-05-01) | ❌ not yet | Add |
| witness-fee-divergence warn-log configured | ❌ not yet | Add |
| frontend CSP headers verified | ✅ done (§15) | Keep |
| Tor onion mirror reachability | ❌ not yet | Add |
This expanded checklist becomes Q9's deliverable — see the Q9 plan in this session's notes.
4. What we deliberately do NOT automate
A list of "could automate, but shouldn't":
- Trade matching. Buyers and sellers pick each other based on reputation, terms, location. Auto-matching breaks the trust model.
- Dispute resolution. Morphit has no arbitrator role
(FAQ
no_escrow_arbitration). A bot that "decides who's right" would be a centralization vector. - Fund release. Non-custodial means the seller releases funds when the buyer pays. No bot can decide when payment has arrived in a non-custodial flow — only the human actually receiving the funds knows.
- Reputation moderation. Feedback is permanent and signed. A bot that "removes spam reviews" is a centralization vector. (Operators can suppress display on their own instance; the chain record is permanent.)
- Owner-key rotation. OPERATIONS.md §8 — must be human with cold-storage backups verified.
- Initial ACT-key passphrase entry on relay first boot.
Documented in OPERATIONS.md §3. Subsequent reboots can use
systemd
LoadCredentialEncrypted=if the operator chooses; first boot is human.
5. Cross-cutting automation ideas
5.1 Health endpoints for external monitoring
The indexer already exposes /v1/health. The relay should
too (today it has a /v1/health but coverage is thin).
Operators can wire these into their existing monitoring
(Uptime Kuma, Statping, etc.).
5.2 Structured log shipping
Both indexer and relay log to journald. Operators wanting remote log aggregation can ship via journald → Vector → any sink. Documented in OPERATIONS.md.
5.3 Automated backup of operator config + key envelopes
Encrypted active-key envelopes are at /etc/morphit/*.key.enc.
The operator should back these up off-host (not the relay's
own filesystem). OPERATIONS.md should add a recommendation
for an automated nightly rsync to a backup destination,
encrypted at rest. Currently silent on this.
Summary table
| # | Task | Auto today? | Recommend automating? | Effort |
|---|---|---|---|---|
| 1.1 | Weekly ACT minting | ❌ Removed (beta.28) | n/a — task no longer exists | done |
| 1.2 | Recurrent BLURT top-up | ✅ Native chain | n/a | done |
| 1.3 | TLS renewal | ✅ certbot | n/a | done |
| 1.4 | Witness fee change response | ❌ Manual | 🟡 Partial — log warn | ~20 lines |
| 1.5 | Stale price feed | ✅ stale flag + log | n/a | done |
| 1.6 | Operator-account balance | ✅ scanner + alerts | n/a | done |
| 1.7 | Relay queue stuck | ❌ Manual | 🟡 Partial — auto-retry 3× | ~80 lines |
| 1.8 | Schema migrations | ✅ on-boot | n/a | done |
| 1.9 | OS package updates | 🟡 Documented | ✅ Add to setup checklist | doc only |
| 2.1 | Backup-seed nudge | ❌ Manual | ✅ Add a 7-day prompt | ~30 lines |
| 2.2 | Counterparty pub verify | ❌ Manual UX gap | 🟡 Tracked (Option 6) | ~150 lines |
| 2.3 | Mark trade complete | ❌ Manual by design | ❌ Cannot — non-custodial | n/a |
| 3.x | Setup checklist | 🟡 Partial | ✅ Q9 deliverable | ~200 lines |
Next steps
- Implement §3.3 (expanded setup checklist) as part of Q9.
- Implement §2.1 (7-day backup-seed nudge). Small.
- Document §1.9 + §5.3 in OPERATIONS.md and RUN-A-MORPHIT-NODE.md.
These can be picked up in any order; they don't depend on each other.