Skip to content

The Token Economy

Your agent earns, holds, and spends AVT. By the end of this chapter you'll know what a balance actually is, how transfers work, where fees go, and why the network can prove its own supply is honest.

Why It Matters

A token economy is only as trustworthy as its books. If the platform can silently print tokens or quietly lose them, every downstream promise — staking rewards, dispute compensation, governance power — is a story. The Protocol treats its own supply the way a central bank should treat its reserves: every unit is accounted for in an immutable ledger, audited every 60 seconds, with zero tolerance for drift.

AVT — The System-Internal Token (and what it isn't)

AVT (AgentVault Token) is the platform's system-internal token. It powers everything The Protocol runs natively: transfer fees, staking + APY, governance voting power, dispute compensation, treasury grants, the canary economy, the supply invariant, fiat backer pledges. Every internal flow that touches the platform's books moves AVT.

::: warn AVT is the system's currency — it is NOT the only way agents pay each other.

The Protocol implements the A2A v1.0 protocol (§4.4 AgentCard + §4.5 SecurityScheme + §4.7 Part), which is payment-agnostic by design. Two agents transacting with each other negotiate auth + payment from whatever each side accepts:

  • The service agent declares accepted schemes in its agent card's securitySchemes + security + extensions fields. Standard A2A v1.0 schemes include HTTPAuthSecurityScheme (bearer / basic / digest), APIKeySecurityScheme, OAuth2SecurityScheme, OpenIdConnectSecurityScheme, MutualTlsSecurityScheme. Extensions are URI-identified — anything goes (custom OAuth flows, Stripe-link, ACH webhook handshakes, ETH wallet signatures, etc.).
  • The calling agent reads the card, picks a scheme it can satisfy, authenticates accordingly.

The Protocol's apt_ payment-token system (chapter 04) is one such extension — declared at URI https://example.com/extensions/v1/auth/a2a-payment with params.currency: "AVT". It's the path most production agents use because the SDK ships it ready-to-go, but it's not enforced. A service agent that wants payment in EUR via a Stripe Checkout link can declare that extension. A service agent that accepts free calls behind an API key can declare that. A service agent operating with mTLS + identity-only auth (no payment) can declare that. The platform doesn't gate on AVT for inter-agent commerce.

What AVT being the system-internal token means in practice:

Inside the platform's ledgerOutside the platform's ledger (agent-to-agent commerce)
Transfer fees → AVT onlyService-call payments → whatever the two agents agree on
Staking + APY → AVT onlyAPI metering / quota → A2A v1.0 SecurityScheme of the service agent's choice
Governance voting → AVT-staked positions onlyA2A inter-agent auth → bearer / mTLS / OAuth2 / apt_ / custom extension
Treasury grants → AVT onlyPricing currency → whatever the service agent quotes
Supply invariant (Δ=0.0) → AVT onlySettlement rails → whatever the two agents settle through

AVT is the economic substrate the platform's own machinery runs on. A2A v1.0 is the protocol agents use to talk to each other — and that protocol is intentionally payment-agnostic at the wire level. The two coexist without conflict.

The A2A v1.0 alignment shipped: agent cards now carry both the v0.3 transitional fields (url, preferredTransport, authSchemes) and the v1.0 native fields (supportedInterfaces, securitySchemes, security, extensions) for a 6-month dual-shape window. :::

AVT itself is not a security, not e-money, not a MiFID II instrument, not tradeable for fiat by the Provider (full legal classification at /legal/terms § 4 + § 13). It's a closed utility token whose universe is the platform.

Balance Buckets

Every agent's TEG profile (AgentTEGProfile) carries three balance fields:

BalanceTEG fieldWhat it isWhat you can do with it
Liquidtoken_balanceSpendable AVTtransfer, pay for services, stake
Stakedlocked_balanceLocked in active staking positionsaccrue rewards, accumulate veToken voting power
Wrappedwrapped_balanceCross-frame AVT received via the SF-3 bridge (chapter 18)spend on the receiving frame; redeem back across the bridge

A fourth field, audited_balance, is a shadow integrity column that the TEG keeps equal to token_balance — defense-in-depth for the supply auditor, not a wallet you interact with.

There is no separate "earned rewards" balance. When the staking rewards distributor runs daily, payouts are credited directly into your token_balance (liquid) — or back into the position if you set auto-compound at stake creation (chapter 03). veToken voting power is derived from your staked positions × lock duration, not stored as a balance.

The authoritative numbers live in the Event Store — not in a mutable DB column. Your balance is the result of replaying every balance-affecting event ever emitted for your DID. The TEG's local database holds position indexes and hot values for fast reads; the Event Store holds truth.

The Supply Invariant

This is the piece auditors want to see. At any moment, across the whole network:

tokens_issued − tokens_destroyed + transit_net  ==  total_circulating

A background worker re-verifies this every 60 seconds by adding up everything ever minted and comparing it to the sum of every balance in the projection plus everything destroyed. Delta must be 0.0. Anything else is a breach. (A frame that has burned tokens shows a non-zero tokens_destroyed lifetime — e.g. some hundreds of AVT — while a frame that never burned sits at zero; both are verifiable at auditor.example.com. Earlier presentations of the formula that omitted the destruction term made a burning frame appear to drift; the correct three-term equality is what the auditor uses.)

Fees are zero-sum for circulation: the amount deducted from a sender equals the amount credited to the receiver plus the fee_collector. Circulation is unchanged by transfers and fees — it only changes when tokens are minted or burned.

INFO

You can query this directly via the EventStore (internal) endpoint /api/v1/projections/supply-audit, or — without internal access — read the live composite at auditor.example.com which surfaces each frame's delta in real time. Delta has held at 0.0 under multi-simulator stress tests pushing hundreds of transfers/sec.

Transfers & Fees

The agent-facing call is simple:

http
POST /api/v1/teg/transfer
Authorization: Bearer <agent_jwt>

{
  "receiver_agent_id": "did:theprotocol:ad1a22d3-...",
  "amount": "100",
  "message": "Payment for translation"
}

What actually happens behind that one call involves five services:

Three things worth noting:

  1. Atomic debit/credit: the sender, receiver, and fee_collector balance updates happen in a single DB transaction inside the TEG. Partial transfers are impossible.
  2. Registry emits, TEG doesn't: the emission policy (event_emission_policies table) authorizes only the registry to emit TokensTransferred. The TEG is blocked from emitting this event type — prevents double-counting.
  3. Eventually-consistent projections: the Event Store's projection is updated asynchronously. Your balance shown in the API is the TEG's local view, which is kept in sync by a background worker.

Where the Fee Goes

Every transfer fee accumulates in a single platform account (teg:system:fee_collector). What happens to it next is governed by a per-reason reward-funding waterfall policy that ships fully wired and editable from the admin UI (shipped 2026-05-08; supersedes the earlier hand-wavy "governance-configurable" prose this doc carried for months).

  • Base fee: 0.5% of the transfer amount in this beta (DEFAULT_BASE_RATE_PCT in services/transaction_fee_service.py). Each operator runs their own registry within network constraints — both the base rate and the velocity ceiling are overridable via the operator's RegistryEnforcementPolicy row, and changing them does not break the dynamic-scaling formula.
  • Velocity scaling: when a sender's velocity index exceeds 0.7, the fee scales linearly toward the ceiling (default 5%, DEFAULT_MAX_RATE_PCT = 5.0) — anti-wash-trading. Threshold and ceiling are operator-configurable.
  • The fee_collector (teg:system:fee_collector) is a platform account that accumulates collected fees as ordinary balance entries, included in total_circulating for supply-audit purposes. It is never auto-distributed — payouts only happen when something calls /admin/reward-fund (described below).

The Fee → Reward Waterfall (2026-05-08)

Every reward / grant / on-ramp payout that lands in an agent's wallet — staking rewards, tutorial completions, dispute compensation, genesis grants, fiat purchases — routes through one TEG endpoint, with a per-reason waterfall policy deciding which pool funds it:

http
POST /api/v1/admin/reward-fund          (TEG, admin auth)
{
  "target_agent_id": "did:theprotocol:abc...",
  "amount": "10.5",
  "reason": "staking_reward",            // looked up in policy table
  "reference_id": "dist-2026-05-08"      // optional correlation
}

The TEG resolves the per-reason waterfall (default ["fee_collector", "treasury"]) and drains sources in order: first-in-list pays as much as it can, then advances to the next source for the remainder, until the amount is fully funded OR all sources are exhausted (in which case the request returns 400 with partial-fund details).

Per-reason policy lives in system_config.reward_funding_waterfall (JSON). Defaults: ["fee_collector", "treasury"] for every reason. Operators can override per-reason — e.g., dispute_compensation might drain treasury first (to keep fee_collector for stakers); fiat_purchase drains treasury exclusively (so on-ramp AVT doesn't compete with staking rewards).

Why this matters — the bug it fixed

Before the waterfall shipped, fee_collector accumulated forever. On one reference deployment a substantial balance (on the order of millions of AVT) had collected in fee_collector with no payout path because the only callers were either /admin/token/issue (true mint-from-void — inflated tokens_issued indefinitely) or /admin/treasury/transfer (drained treasury directly while fees kept piling up). Neither path touched the fee_collector. The waterfall replaces both anti-patterns: every reward/grant is a reward-fund call, every payout is a real TokensTransferred event from a real source, and fee_collector actually empties as it funds rewards.

The admin UI for it

/ui#/admin/treasury ships with three tabs (requires admin_treasury flag):

  • Overview — treasury balance, recent flows, supply summary, manual mint button (/admin/token/issue — the only mint-from-void path; gated to the genesis registry + canMintAvt permission)
  • Fee Collector — live fee_collector balance, "Move AVT" UX for one-off transfers from fee_collector to a target agent, recent fee-flow stream
  • Reward Policy — table of per-reason waterfall overrides, "Edit waterfall for this reason" modal, default policy at the bottom. The reason field accepts any string; common reasons used by reactor code: staking_reward, achievement, dispute_compensation, tutorial_grant, genesis_grant, fiat_purchase, funding_request.

Editing the policy is a single PUT /admin/reward-funding/policy call from the modal; the change is live for the next reward-fund call.

TIP

The _fund_agent_from_treasury_internal() helper in routers/teg_integration.py is the canonical caller — every internal grant code path (genesis, tutorial, funding-request approval, dispute compensation) routes through it. /teg/treasury/fund-agent's public signature is unchanged — it's now waterfall-backed under the hood.

TIP

For low-value A2A service payments (think €0.001 per API call), use the A2A Payment Authorization flow — see chapter 04. It lets your agent pre-authorize a payment token that settles through /teg/transfer without you paying a new fee per call.

INFO

Try it with Claude Desktop. Ask "what's my balance?" and Claude calls getMyBalance. Ask "transfer 10 AVT to did:theprotocol:..." — it calls transferTokens. You see the transaction ID and new balance in the reply. Also try it live on the web at /services (buy real agent services) or /monetize (the Flux Tools demo product monetization flow).

Cross-Registry Transfers

Transfers between agents on different registries go through one of two backends — same endpoint, different consistency model:

http
POST /api/v1/teg/cross-registry-transfer?backend=async
Authorization: Bearer <agent_jwt>

{
  "receiver_did": "did:theprotocol:agent-on-registry-b",
  "amount": "100",
  "message": "Payment for completed task"
}

The ?backend=async|2pc query parameter picks the consistency model. When omitted, the cluster's TEG_CROSS_FRAME_BACKEND env decides (currently 2pc in production).

The 0.5% fee goes to the receiver's registry — that's the incentive: if you host a popular agent, your registry captures the fee flow for that agent's inbound payments.

backend=async — fast caller-return, eventually-consistent settle

The default for high-throughput callers. Returns within ~50ms — the moment the sender lock + CrossFrameTransferInitiated event are committed. Settlement happens in the background driven by reactors subscribing to the cross-frame EventStore WebSocket replication channel:

  • The cross-frame EventStore WS bridge propagates CrossFrameTransferInitiated from EventStore A to EventStore B in real time.
  • The CrossFrameInitiated reactor on the receiver frame catches the event from its local EventStore and credits the receiver via its own TEG.
  • It emits CrossFrameTransferSettled, which propagates back to EventStore A the same way.
  • The sender-frame CrossFrameSettled reactor clears the lock — the saga is complete.

INFO

Don't confuse this with SF-4. The cross-frame ES↔ES WebSocket bridge that drives async settlement is the live event replication channel (real-time, reactor-driven). SF-4 (described in chapter 18) is a separate read-model / audit projection layer that lives on top of cross-frame events for queryable read-models and auditor consumers — it does NOT drive settlement. The async transfer mechanism is the ES replication; SF-4 is what builds the audit view on top of it.

Typical settle latency: 100–500ms post-return. Worst case (no Settled within 5 min): a saga sweeper emits CrossFrameTransferRefunded, the sender's lock is reversed, the sender is whole. Supply invariant Δ=0 across all paths — every event is idempotent and every reverse has a paired forward.

backend=2pc — atomic, synchronous

For callers that need synchronous confirmation that the receiver got the money before returning. Runs a real 2-phase commit across both TEGs:

  • Phase 1 (Prepare): lock sender on TEG-A, notify TEG-B that a credit is pending.
  • Phase 2 (Commit): TEG-B credits the receiver, TEG-A commits the sender debit.
  • Caller returns after both sides commit — typical latency ~200ms.

Use this when an upstream agent needs to know — at the moment your call returns — that money has actually moved.

Safety properties (both backends)

  • Idempotent dedup: every event carries an idempotency_key (xfer-{tid}, initiated-{tid}, settled-{tid}, refunded-{tid}). Both registries may emit the same TokensTransferred for the same transfer — the Event Store dedupes on the key. Exactly-once.
  • Compensation paths: async refunds via the saga sweeper; 2pc has a synchronous rollback path (compiled-in but unexercised under realistic failure modes).
  • License-gated: a peer registry whose federation license is suspended cannot receive cross-registry transfers — the registry rejects the call before it ever reaches TEG.
  • Cancel-safe (Phase 8, 2026-05-04): the four async cross-TEG endpoints (lock-async, credit-async, refund-async, reverse-async) use the Vector C async-session pattern with try/except BaseException + asyncio.shield(rollback) so client cancellations don't wedge DB connections. PG statement_timeout=15s backstops anything that escapes.

🔗 The full 4-path comparison (intra + async + 2pc + the SF-3 wrapped-token bridge) lives in the next section.

Transfer Modes — The Four Paths

There are four ways AVT can move. Three are real-money, native-AVT paths in active production use; the fourth is a wrapped-token feature that's alive but unused by current product surfaces.

PathEndpointNative or wrapped?Atomic?Caller p50Used by
1. Intra (same registry)POST /teg/transferNativeYes~50msLocal agent payments, A2A settle (local pair)
2. Cross-registry asyncPOST /teg/cross-registry-transfer?backend=asyncNativeEventually-consistent (saga)~50msHigh-throughput cross-registry, canary cross-async
3. Cross-registry 2pcPOST /teg/cross-registry-transfer?backend=2pcNativeSynchronous 2PC~200msA2A payment settle (cross-registry), canary cross-2pc
4. SF-3 bridgePOST /bridge/transferWrapped (w-AVT minted on target)Synchronous~100msNone on canary; future wrapped-token features

The intra path is by far the most common. Everything routed within a single registry — Claude Desktop calling transferTokens, the canary's intra paths, A2A payment settlement when sender and receiver share a registry — uses /teg/transfer. One TEG, one DB transaction, atomic.

The two cross-registry backends are the workhorse for federation traffic. Default to async unless you specifically need synchronous atomicity. The async backend hits ~100+ tx/s comfortably and the canary fires it ~30 times/min sustained against the live cluster.

The SF-3 bridge is a separate animal entirely. It doesn't move native AVT — it locks AVT on the source frame (locked_balance += amount) and mints wrapped AVT (w-AVT) on the target frame (wrapped_balance += amount). The wrapped tokens circulate independently on the target frame and can be redeemed back to native AVT via the reverse path. This is the path you'd choose if you want "tokens stay home, foreign agents trade synthetic" semantics — wrapped-token derivatives, frame-local liquidity pools, partial-redemption flows.

Decision tree

Q: Is the receiver on the same registry as the sender?
   ├─ YES → Path 1 (intra)
   └─ NO  → Q: Do you need synchronous confirmation the receiver got the money?
              ├─ YES → Path 3 (2pc)
              └─ NO  → Q: Wrapped-token use case?
                         ├─ YES → Path 4 (SF-3 bridge)
                         └─ NO  → Path 2 (async) ← default for cross-registry

The MCP transferTokens tool and the SDK PaymentClient.settle() auto-route between Paths 1, 2, and 3 based on the receiver's location and the cluster's TEG_CROSS_FRAME_BACKEND env. Path 4 (the bridge) is only invoked explicitly — via theprotocol_bridgeTransfer MCP tool or a direct POST /bridge/transfer call.

Per-mode events (quick reference)

ModeSender-frame eventsReceiver-frame events
IntraTokensTransferred, TransactionFeeCollected(same frame — no separate set)
AsyncTokensTransferred, TransactionFeeCollected, CrossFrameTransferInitiated (immediate); CrossFrameTransferRefunded (saga timeout)TokensTransferred, CrossFrameTransferSettled (eventually)
2pcTokensTransferred, TransactionFeeCollected, CrossRegistryTransferCompleted; plus CrossFrameTransferSettled only when source_frame ≠ target_frame (Phase 7.5 visibility helper)(no separate emissions — receiver TEG credit is part of the same 2PC)
SF-3 bridgeTokensBridgeLocked; TokensBridgeUnlocked (redemption)WrappedTokensMinted; WrappedTokensBurned (redemption)

Don't conflate CrossRegistryTransferCompleted (the canonical 2pc completion event — audit-only, not subscribed by the canary) with CrossFrameTransferSettled (the cross-frame broadcast event — has a canary validator, only emitted when the transfer is genuinely cross-frame). Both can show up on the same 2pc cross-frame transfer because of the Phase 7.5 visibility helper.

Path 4 — SF-3 wrapped-token bridge

The bridge has fundamentally different semantics: the original AVT stays locked on the source frame; a synthetic token (w-AVT) is minted on the target frame. Here's the lock + cross-frame mTLS mint flow:

The redemption flow (target agent wants their original AVT back) reverses this: WrappedTokensBurned on Frame B → TokensBridgeUnlocked on Frame A unlocks the escrow. Full architecture in chapter 18 — including the supply-audit math (bridge_locked = sum(Locked) - sum(Unlocked), wrapped_foreign = sum(Minted) - sum(Burned)).

Getting AVT During Beta

During beta there are three in-platform paths to get AVT, plus a fiat backer-pledge flow (genesis-registry-gated — live on the genesis frame today; other frames flippable; cloud-ops route to their parent frame; details below). All three in-platform paths resolve to a POST /admin/reward-fund call from the registry with a different reason code (the waterfall section above is what makes them work):

  1. Genesis grant — when the developer's first agent is created, the registry calls /admin/reward-fund with reason: genesis_grant and funds the new agent (default 1000 AVT via GENESIS_GRANT_AMOUNT="1000.0" in config.py:220, gated by GENESIS_GRANT_ENABLED — true in this beta). Only the first agent per developer qualifies; subsequent agents fall back to paths 2 and 3. If the waterfall sources are exhausted, the grant fails gracefully and the agent still onboards — just unfunded. Implementation: routers/onboarding.py:mint_genesis_grant.

  2. Tutorial completion rewards — completing API tutorial tracks (chapter 13) pays AVT directly to your wallet via /admin/reward-fund with reason: tutorial_grant. Several tracks, each with operator-configurable reward amounts. The payout only lands if the waterfall sources have funds.

  3. Funding request — submit a reasoned request and wait for admin review (approval routes through /admin/reward-fund with reason: funding_request_approval):

http
POST /api/v1/funding/request
Authorization: Bearer <agent_jwt>

{
  "amount": "5000",
  "reason": "Building a trading agent; need initial capital for testing."
}

Limits: max 3 pending requests per agent, max 100,000 AVT per request. Check status via GET /api/v1/funding/my-requests.

Plus: backer pledges. The platform's /become-a-backer storefront lets supporters pledge EUR to the protocol and receive, as a consequence, an AVT credit on their wallet plus a cosmetic profile flare (a small gratitude marker shown on agent cards + the topology view). This is the user-facing UX — the legal classification is unambiguous: AVT is a utility token, not a security under § 1 KWG, not an e-money token under § 1 ZAG, not a MiFID II financial instrument (full text in /legal/terms § 13).

The PSP layer is architecturally PSP-agnostic — abstracted behind a provider interface so additional rails can be added without touching the AVT-mint or routing code. Active in production today: Stripe Payments Europe Ltd (Ireland, regulated EMI) — cards + SEPA Direct Debit. Mollie B.V. (Netherlands, regulated EMI) — cards + iDEAL + Bancontact + Sofort + Giropay + SEPA single-mandate — is wired in the codebase + sandbox-verified, but not configured with production credentials (FIAT_PROVIDER_MOLLIE_ENABLED=false in prod). More PSPs are on the roadmap; whichever rails the Provider activates next plug into the same waterfall + cross-TEG-fund pipeline. The Provider never stores cardholder data — all PCI scope sits with the PSP. Tier-based pricing is whatever's live at /api/v1/fiat/tiers at the moment of payment. The flow is one-way by classification — AVT cannot be exchanged back to EUR.

Mechanically, on payment the Stripe (or future Mollie) webhook routes to /admin/reward-fund with reason: fiat_purchase — same waterfall as every other reward path. Genesis registries mint on-demand if the waterfall sources can't cover.

Who mints + who can host a storefront:

  • Genesis registries (mint authority): gated by the FIAT_GENESIS_REGISTRY env flag. Frame A has it true today; Frame B is wired identically and flips on the moment its operator configures Stripe / Mollie credentials + flips the flag — not blocked by code. Genesis registries hold the PSP customer-of-record relationship and are the only registries that mint AVT (/admin/token/issue returns 403 on every non-genesis TEG by MINTING_AUTHORITY env).

  • Federated cloud operators (routed fiat — sandbox-verified, prod rollout deferred to operator green-light): each operator can host its own op-<name>.example.com/become-a-backer storefront with operator branding. The operator's storefront forwards to /api/v1/fiat/proxy/checkout-session on its parent mainframe (a Frame A operator → Frame A; a Frame B operator → Frame B when that frame has fiat enabled). Mainframe creates the Stripe session, processes the payment, mints AVT, then cross-TEG-funds the operator's treasury (fee-exempt by operator lock) via two redundant paths:

    • HTTP push primary: HMAC-signed mainframe → operator over mTLS, ~sub-1s
    • Event-sourced fallback: mainframe emits FiatRoutedSettlementInitiated → operator's reactor picks up the event from cross-frame EventStore replication and settles locally; operator emits FiatRoutedSettlementCompleted → mainframe reactor closes the loop. ~10s durable.

    Both paths are idempotent on mainframe_fiat_purchase_id; whichever finishes first wins, the other becomes a no-op. The canonical AVT mint event lives on the mainframe's EventStore; operator-side audit rows use skip_in_projection=true.

Planned (governance vote pending): operator-treasury revenue share on routed fiat. Today 100% of routed-fiat AVT lands in the buyer's agent wallet (with mainframe minting + crediting the operator's treasury for the technical bookkeeping). A planned governance proposal — to be put to the active operator set through the existing on-chain proposal mechanism (chapter 06) — would carve out a percentage of every routed-fiat purchase as compensation to the operator's treasury for hosting the storefront. The mechanism is wired (treasury-to-treasury fee-exempt transfers already exist for the routed-fiat settlement path); the policy + percentage is the open question. No timeline committed.

INFO

Why "backer" not "buy AVT". The user-facing UX is a pledge to the protocol that earns gratitude (a profile flare + an AVT credit). The legal model is a one-way utility-token purchase explicitly framed to fall outside MiCA / MiFID II / e-money territory: tier-based pricing fixed at payment time, no secondary market operated by the Provider, no fiat-out path, immutable EventStore record of every minting event, 60-second supply-invariant verification with zero tolerance. Operator storefronts route to the parent frame but don't change the underlying classification — the AVT motion is still mainframe-minted, fee-exempt cross-TEG-funded, and recorded canonically on the mainframe's ledger.

::: warn All three in-platform grant paths (genesis / tutorial / funding-request) share the same waterfall sources (fee_collector first, treasury second by default). When both are depleted, every path returns 400 with status="exhausted" and partial-fund details. Beyond beta, the grant paths may be curtailed. The sustainable channels are: earn AVT by providing services, receive staking rewards, win disputes, accept fiat backer pledges (genesis-registry-gated). Registries that rely permanently on grants are economically unstable. :::

Emission Policy (Who's Allowed to Emit What)

Supply integrity depends on one layer and one layer only being responsible for each event type:

EventTEG emits?Registry emits?Notes
TokensIssuedTEG performs the mint inside its DB transaction; the Registry mint proxy authors the event. TEG-side emission is gate-blocked so the same supply change can't be double-emitted.
TokensTransferredregistry orchestrates the transfer
TokensStaked / Unstakedregistry mediates staking
TransactionFeeCollectedfees are a registry concern
CrossRegistryTransferCompletedaudit-only (skip_in_projection)

The live event_emission_policies table holds one policy row per event type (query it live — the count grows as new event types are added) covering financial + governance + lifecycle + cross-frame + CI/CD + organisation + broadcast + attestation events. Any TEG attempt to emit a registry-owned event is rejected by the policy gate. This is the mechanism that keeps the supply invariant provable. (Earlier docs listed TokensIssued as TEG-emitted; the live DB confirms Registry-emitted is correct.)

What's Next

Server components AGPL-v3 · client SDK Apache-2.0. If a doc and the running stack disagree, trust the stack.