15 KiB
Operator trust tooling — design discussion
Status: Design ratified. Items 1 + 2 ✅ SHIPPED. Item 3 (orderbook comparison view), Items 4 + 5 (advisory op + governance process) remain open.
- Item 1 —
verify.jsongenerated at build time byscripts/build-verify-json.mjs(repo root), served as a static asset. Captures version + git commit + build timestamp + operator tag + hash manifest. - Item 2 —
/about-this-instanceroute atapps/web/src/routes/[lang]/about-this-instance/+page.sveltevisualizes the verify.json for users. Does not yet cross-check against the on-chain release-op (intentionally — the cross-check is followup work, this page just makes the claim legible). - Item 3 — orderbook comparison view: not shipped.
- Item 4 —
morphit_operator_advisory_v1op: not shipped, blocked on the OP-TRUST Q1-Q5 governance discussion. - Item 5 — community advisory governance: blocked on Q5 maintainer/Agorise decision.
Date: 2026-04-21 (design); shipped iteratively. Interacts with: ADR-0013 (operator incentives), ADR-0011 (dynamic fee model, operator payout path).
Why this document exists
The revisit list carried "operator-shutdown tooling" for a while. After writing a first draft, the framing itself turned out to be wrong. We can't shut down operators. The architecture is specifically designed so that nobody can. This document replaces that framing with an honest one: operator trust tooling. We're not stopping anyone. We're helping users see which operators deserve their trust.
Morphit's architecture is intentionally federated: anyone can stand up an instance (SvelteKit frontend + indexer + relay) and serve users. The indexer reads the Blurt chain as its source of truth; the frontend renders from the indexer's API; the relay broadcasts ops on users' behalf. Users choose which operator's frontend they visit.
This design is censorship-resistant by construction. It's also permanent: once the software exists and the chain exists, operators exist, and nothing we do can change that. If an operator acts in bad faith, we cannot technically or legally remove them. We can only inform the users.
What "stopping" a bad operator actually looks like
It's worth being explicit, because the intuition from centralized services is wrong.
What we cannot do:
- We cannot make their server stop. We have no administrative access. They pay for their own hosting.
- We cannot block users from reaching them. There is no central DNS, gateway, or CDN that we operate.
- We cannot stop them from reading the Blurt chain. It's a public blockchain.
- We cannot stop them from collecting operator fees. Once ADR-0013 ships, every operator earns a fee share via the same protocol rules. If users post orders through their frontend, those fees flow to them regardless of our opinion of them.
- We cannot legally compel them. They're anonymous. No KYC, no jurisdiction, no company, no address.
What we can do:
- Warn users via channels we control:
morphit.io's homepage, Matrix, the@morphitBlurt account, the signedmorphit_release_v1op on the chain. - Publish verification tools so users can check an instance themselves — e.g., the signed bundle hash from the release-op lets any user detect an instance serving a tampered build.
- Compete. Build a better instance than the bad actor's. Users who care about quality pick the better instance.
The honest consequence: a better-looking operator with better uptime and better UX should earn more than morphit.io. That's correct. That's the market working. The way we protect morphit.io's relevance is by making it actually better, not by suppressing competitors.
This framing matters because every "shutdown" tool we could imagine either (a) doesn't actually shut anything down, or (b) creates a central point of control that a government could pressure. We build (a)-type tools. We refuse to build (b)-type tools.
Threat model — three tiers of bad operator
"Bad operator" covers three categorically different behaviors and the appropriate response is different for each. Muddling them leads to either tyranny (treating the selfish operator as if they were lying) or uselessness (treating the lying operator as if they were selfish).
Tier 1 — Selfish operator
Behavior: Runs a technically correct node but doesn't participate in the ecosystem as intended. Examples:
- Strips the
operator_tagfield from orders they broadcast, so fee shares flow to@morphit-feesrather than to the operator who actually served the user (ADR-0013 interaction). - Claims operator status without paying the registration fee.
- Promises 99% uptime and delivers 50%.
User impact: Low. Users see correct data; the only damage is to operator economics. The user doesn't know or care.
Appropriate response: Economic, not policing. The market handles Tier 1 without any tooling from us.
Tier 2 — Censoring operator
Behavior: Silently filters what users see. The operator doesn't fabricate anything — they just omit. Examples:
- Hides orders from accounts they don't like.
- Hides orders in a particular fiat currency.
- Hides the cheapest listings to steer traffic toward their own.
User impact: Medium. The user gets some of the truth but not all of it.
Appropriate response: Verifiability. Make the omission detectable so a user (or a watchdog) can compare one operator's view to another's and see the discrepancy. This is not enforcement — censoring operators keep running — but their filtering becomes visible.
Tier 3 — Lying operator
Behavior: Actively fabricates data. The operator's frontend returns orders that don't exist on-chain, or mis-prices orders that do exist, or ships a phishing fork of the UI that exfiltrates seed phrases.
User impact: High. Users lose funds, lose keys, or trade based on false data.
Appropriate response: Truth-amplification. Warn users away via every channel we control, and publish tools users can run to verify an instance themselves. The operator keeps running — to an increasingly empty parking lot as users check the warnings.
Design primitives we already have
Before adding anything, catalog what's there:
Release-op trust anchor (ADR-0008, shipped)
The morphit_release_v1 op is signed by @morphit with
the pinned posting pubkey. Its payload includes:
version(semver)hash_manifest(asset hashes of the frontend bundle)endpoints(RPC endpoint rotation set)signature(optional additional signature)
The hash_manifest is the key primitive. Every legitimate
frontend instance should serve a bundle whose hash matches
what @morphit's latest release-op published. A user
visiting a tampered fork can check the hash and see a
mismatch — in theory. Today this requires a tool that
doesn't exist yet (see item 1 below).
Gap we need to close: a user-facing "is this instance legitimate" check that uses the release-op hash_manifest.
releases.invalid_reason audit trail (shipped this session, Finding J fix)
Rejected release ops are now recorded with a specific
invalid_reason. If @morphit's posting key is ever
compromised, the audit trail distinguishes:
signer_not_official_account(phishing / impersonation; no real key compromise)signer_no_single_posting_key(unusual chain auth setup; not necessarily malicious)pubkey_mismatch(pinned value differs from what's on-chain — signal to rotate keys immediately)
A key-compromise event would require a config update to
all operators: bump the pinned officialPostingPubkey to
a newly-rotated value. Currently there's no chain op that
signals "the @morphit posting key has been rotated" —
operators would coordinate out-of-band (Matrix, email).
Gap we need to close: a key-rotation signaling op or a documented out-of-band procedure.
Operator registration (ADR-0013, ✅ shipped 2026-05-02)
ADR-0013 was Accepted-and-implemented in early May. We now have:
- a list of registered operators on-chain
- a registration fee they've paid
- a unique operator tag claimed by each
Registration is voluntary — an operator can run an instance without registering. Registration is how they get their fee share, not how they get permission to operate.
Gap closed: ADR-0013 accepted-and-implemented 2026-05-02 (registration, tag claim, payout pipeline all in place).
Proposed tooling (minimum viable)
Short-term, no new chain ops (shippable now)
1. Instance-verification endpoint (frontend).
Every frontend instance exposes /verify.json:
- the current release-op version string
- the hash of the frontend bundle this instance is serving
- the git commit hash of the frontend build
- the operator's claimed tag (null for unregistered instances)
A user or a watchdog fetches this and compares to the release-op value on chain. Mismatches are visible without trusting the instance's own claim about itself.
Scope: ~50 lines of static-build-time generation plus one JSON endpoint. No chain changes.
2. "About this instance" page.
A /about-this-instance route on every frontend that
visually presents the verify.json data to the user:
"You're on:
morphit.agorise.world. This instance claims to be running Morphit v2.3.1. The release-op on chain says v2.3.1 hash isabc123…; this instance servesabc123…✅"
When the hash matches, the check is green. When it
doesn't, the check is red and the page shows what the
expected hash is and links to morphit.io and
morphit.agorise.world as known-good alternatives.
Scope: ~200 lines of Svelte + i18n. No chain changes.
3. Comparison view.
The orderbook page gains a "compare with another instance" toggle. User enters a second instance URL; the page fetches both orderbooks and highlights anything one has that the other doesn't.
Scope: ~400 lines of Svelte + a shared-schema
expectation that both instances serve the standard
/v1/orderbook API. No chain changes.
Medium-term (needs ADR-0013 first)
4. Signed operator advisory op.
morphit_operator_advisory_v1 signed by @morphit
(release-op trust anchor: signer must be official
account, posting pubkey must match pinned value). Payload:
{
"operator_tag": "<tag or null for origin-based>",
"origin": "<hostname or null for tag-based>",
"advisory": "warn" | "do-not-use",
"reason": "short human-readable string",
"evidence_url": "optional link to proof"
}
Frontend instances read the latest advisory list and, if the current origin or claimed tag is on it, show a warning banner:
"⚠️ This instance is on a community advisory list. Reason:
<reason>. Consider visiting morphit.io or morphit.agorise.world instead."
This is not a shutdown. The instance keeps running. The warning is informational. A lying operator could even serve a frontend that hides the advisory banner — but users can detect this via item 1's on-chain hash check.
Scope after ADR-0013: ~100 lines of indexer handler,
a new operator_advisories table, the release-op schema
addition, ~50 lines of frontend check.
Long-term (needs governance process)
5. Community advisory maintenance.
Someone has to decide what goes on the advisory list. This is a governance question, not a technical one. The very high bar for inclusion: demonstrated Tier 3 behavior (fabrication, phishing), not disagreement with project direction. Requires a documented process with evidence requirements and an appeal path.
The advisory list should NOT be a tool for suppressing Tier 1 freeloaders or Tier 2 censors. Tier 1 is handled by the market; Tier 2 is handled by item 3's comparison view.
What we explicitly don't want
Anti-patterns to avoid:
-
Pre-emptive blocking of unregistered operators. A new operator should be able to stand up an instance without asking permission. Requiring prior approval destroys the federated property.
-
Central domain-level filtering. Morphit doesn't run DNS for operator instances. Any "shutdown" via DNS requires us to run DNS, which is centralization we don't want.
-
Irreversible operator advisories. Every advisory must be reversible by a follow-up op from
@morphit— otherwise a compromised key could permanently destroy legitimate operators. -
Trust-on-first-use of arbitrary instance JSON. The
verify.jsonendpoint MUST cross-check against on-chain release-op data; relying on the instance's own claim about itself defeats the point. -
Any tool framed as "shutdown." We don't have that power. Framing tools as if we did creates expectations users will feel betrayed by when the bad operator keeps running.
Operator anonymity — design invariant
Operators are anonymous by default. This is a feature:
- Registration requires only a Blurt account. No name, no email, no address. The operator picks a tag and pays the registration fee. Done.
- The
contact_urlfield on operator profiles is optional. An operator can populate it with a Matrix handle, a Nostr pubkey, an onion address, or nothing. They choose what to expose. - Nothing in the public UI identifies operators
geographically or personally. Operators are
pseudonymous. The
/operatorspage shows the tag, registration date, on-chain stats, and the operator's own chosen contact link (if any). No country flags, no city names, no real names. - This invariant also applies to morphit.io itself. The instance is identified as "operated by Agorise," which is a project-level pseudonym. No individual person is named anywhere user-visible.
If a jurisdiction ever pressures a known person behind an operator, that person hasn't been made known by us. Operator anonymity is protection against legal coercion.
Open questions (need maintainer / governance decisions)
OP-TRUST Q1: What evidence threshold justifies a
morphit_operator_advisory_v1 op? Who reviews the
evidence? Is there an appeal path?
OP-TRUST Q2: Does the release-op gain an
advisories field now, or do we wait for the first
concrete incident to force the design?
OP-TRUST Q3: If @morphit's posting key is
compromised, what's the recovery path? A hardcoded
secondary pubkey in officialPostingPubkey config
that operators can roll to? A published recovery
procedure that assumes out-of-band communication?
OP-TRUST Q4: Should the short-term tooling (items 1, 2, 3) ship even if ADR-0013 is still pending? I'd argue yes — they're independent of the operator- incentive design and help Tier 2/3 detection regardless.
OP-TRUST Q5: Who maintains the advisory list? Project maintainer? Agorise? A multi-signature committee? Once this is more than one person, it's a governance structure that needs its own consideration.
Recommended next step
Before writing an ADR: build items 1 and 2. Both are independent of every open question above, both help Tier 2/3 detection immediately, and both create concrete artifacts that users and operators can reference when OP-TRUST Q1-Q5 get discussed.
Item 3 is independent but bigger UI work — probably a second pass after items 1 and 2 land and we see how users engage with them.
Items 4 and 5 need ADR-level decisions first. Item 5 specifically needs a governance process that doesn't exist yet, and that's the bigger blocker.