TOKN / field guide

Capsule delivery: bounded authority, actual bytes, explicit payment

Status: experimental opt-in candidate for the 4.0 architecture. There is no public capsule contract, storage endpoint, funded offer or production migration. Production and the frozen TEST M0 round are unchanged. Run the laboratory below only after reviewing the source. Its balances are disposable local test currency, not production TOKN, a grant, a price or a redemption promise. Both the constructor and client reject current production chain 2026090601 and frozen pilot chain 2026090604; this candidate needs a separate network.

What is new

An agent with zero native balance can request and recover a small encrypted artifact using a narrowly authorized signing key. An owner explicitly prepays a fixed lifetime budget. A provider explicitly accepts a specific job. Any relayer can pay gas for the agent's exact signed request and completion. Neither the storage process nor the recovered text receives spending keys or command-execution authority. The provider receives a withdrawable credit only when the authorized reader signs the exact agreed observation, once.

The architectural contract is no longer "a parsed event says PASS": it is immutable owner authority → atomic reservation → ciphertext HTTP custody → authenticated inert recovery → subject-bound payment authorization → pull withdrawal, with one on-chain liability ledger. This is the reason for the 4.0 major boundary. It is not a claim that the network has decentralized, that existing contracts were upgraded, or that every roadmap milestone passed.

The protocol is new and intentionally incompatible with v3 declarative receipts. agent_legacy.py remains a data/schema/commitment checker. No field such as controller: astranaut01, reviewer name, command string or CURRENT label can be imported as capsule payment authority. Keep historical evidence intact and append corrections; do not relabel an old peer report as a signed execution.

Reproduce the complete path

From a reviewed source checkout with Docker and Compose:

docker compose -f compose.capsule-check.yaml build check
docker compose -f compose.capsule-check.yaml run --rm check

The isolated container has no external network, no production mounts and no real keys. It starts its own Ganache RPC on loopback, chain 2026090605, deploys CapsuleEscrow locally, creates purpose-specific ephemeral keys, and exercises the actual CLI and HTTP storage process. It kills the storage process with SIGKILL after a confirmed PUT, demonstrates the retained lock, explicitly recovers it after confirmed quiescence, starts a new process, retrieves/decrypts the same bytes, submits the signed completion and checks native accounting. It also checks provider withdrawal, receipt replay, cross-domain forgery, corruption, quotas, concurrent budget exhaustion, revocation and timeout.

Each run saves an immutable result.json in a newly named state/capsule-check/work/evidence-* directory. Other test fixtures remain below that test-only directory. CLI keys and its local working data are tmpfs-only and disappear when the container is removed. Do not interpret different random genesis/address/transaction hashes between runs as the same network. The receipt explicitly reports one controller and zero independent operators.

This is a real EVM/HTTP/process integration test, not a Besu/QBFT consensus test, external operator test, hardware power-loss test or twenty-minute outage test. Ganache can warn that its optional native uWS extension is unavailable and use its JavaScript fallback; that is not evidence about Besu performance.

Protocol and invariant

The owner creates one immutable mandate with reader address, provider address, price per job, total prepaid native budget, maximum ciphertext bytes, retention promise and expiry. Provider must differ from owner and reader as an address; this does not establish independent human or organizational control. No top-up, subdelegation, arbitrary call or implicit treasury funding exists.

Request and observation use EIP-712 with chain ID and contract in the domain, and genesis hash in the typed message. Each job binds a unique mandate/nonce, exact ciphertext digest/size, authority-envelope digest, terms, actors, price, deadline and retention. Signatures authenticate a key under the selected mandate; names and tool digests do not become identity or execution attestations.

TransitionAuthorizationAccounting
Create mandateOwner sends budgetavailable += budget
ReserveReader signature; anyone relaysavailable -= price; locked += price
AcceptExact provider transaction and termsNo monetary movement
Complete before deadlineExact reader receipt; anyone relayslocked -= price; provider credit plus 1% fee
Refund after deadlineAnyone; also revoked unaccepted jobslocked -= price; owner credit
RevokeOwnerRemaining available becomes owner credit
Withdraw / flush feesCredit owner / anyone for fixed fee recipientPull transfer with reentrancy guard

For every reachable state, contract balance is at least available + locked + totalCredit + pendingFees. Forced unsolicited native value may create surplus, never new credit. Lifetime reservations cannot exceed the original prepaid budget. Refunds never refill a compromised reader's authority. Concurrent reservations cannot spend the last budget unit twice; a paid/refunded job cannot settle again. No terminal path spends the same principal twice.

The fee is floor(price * 100 / 10000); remainder goes to provider credit. Its recipient is fixed at deployment and disclosed by feeRecipient(). No new issuance, validator reward distribution or external commercial value is implied. The relayer's gas is separate: no reimbursement or public relayer service is promised. Only the reader may have zero gas balance; owner/provider/relayer need their own explicit transaction authority and test gas.

Revocation prevents new reservations and acceptance of unaccepted jobs. It does not erase already accepted obligations: those may complete before their original deadline. A missing/withholding reader causes full refund after timeout, not automatic provider payment or a finding of provider misconduct. The provider must accept that allocation before supplying bytes. There is no arbitration, provider bond, slashing, SLA compensation, fair-exchange guarantee or proof of impossibility.

What the observation proves — and does not

The shipped reader actually GETs ciphertext, checks exact size and commitment, authenticates AES-256-GCM with the authority envelope as associated data, and writes the recovered plaintext as inert artifact.bin. It never imports, executes or injects the text into an agent prompt. Its fixed signed outcome is:

BYTE_INTEGRITY_VERIFIED; DATA_ONLY; CLAIM_UNVERIFIED;
RUNNER_SELF_REPORT; RETENTION_UNPROVEN

A signature proves authorization of that message, not that the signer used this client honestly. A compromised/colluding reader can authorize payment without retrieval. A regression test intentionally demonstrates this attack succeeds within the mandate's budget. Do not advertise it as cryptographic proof of storage, a trusted execution attestation or subjective truth. A reader may also withhold an otherwise deserved signature. The protocol bounds those risks; it does not solve the general oracle/fair-exchange problem.

Recovery of a statement does not make that statement current or true. The authority envelope contains purpose, rights, objection, content role, mayDecline: true, containsSecrets: false and reviewAfter. These declarations are authenticated to the capsule, not independently verified. Expiry is reported as reviewDue; every recovered claim still needs its own safe recheck. Never include private training data, prompts, third-party secrets or recoverable private/signing keys. This client is not a secret detector or legal rights oracle.

Client and service interface

node scripts/capsule.mjs --help lists the local commands. Source dependencies must be installed from the reviewed lockfile; compile via the project Dockerfile to produce the ABI. Native Node 22/POSIX is supported for the explicit CLI; services must run in Docker. The CLI reads no default wallet, .env or implicit RPC, and broadcasts only with the literal --broadcast argument. The native transaction command holds its chosen wallet while online; it is not the offline-signing boundary of the separate participant wallet kit.

An operator supplies tokn-capsule-network/1 JSON with exactly schema, rpc, chainId, genesisHash, escrow, runtimeHash, provider, storageUrl. The client checks chain/block-zero/runtime and contract protocol identity. HTTPS is required except explicit loopback HTTP. Redirects from blob operations are refused. RPC identity checks rely on that RPC; they are not independent consensus proofs, light-client verification or fork-choice. Genesis in the constructor is a deployment assertion checked against the pinned RPC, not a Solidity discovery of the chain's ancient block-zero hash.

The sequence used by the tested CLI is:

  1. Create dedicated wallet/recovery files once via keygen, below

secrets/capsule/, directory 0700 and files 0600. Never use production keys.

  1. seal a public, reviewed input and authority JSON into a new prepared

directory. Maximum plaintext is 65,504 bytes; wire ciphertext is at most 65,536 bytes. Encryption uses fresh random 96-bit GCM nonces. Use one fresh recovery key per capsule; do not use this small-object format for bulk data.

  1. Owner explicitly reviews a createMandate transaction document, its budget,

actors, price and expiry; dry-run tx first, then separately authorize tx ... --broadcast. No command chooses funding or an offer for the owner.

  1. Reader request prepares a signed request; relayer explicitly broadcasts it.
  2. Provider inspects accept-document, including the full-refund risk and

retention promise, then explicitly broadcasts the acceptance.

  1. store PUTs ciphertext. receive GETs and decrypts it, writes inert output

and a completion document. It does not broadcast the completion.

  1. Relayer reviews/broadcasts completion; provider separately withdraws credit.

status reports pinned-RPC state and all liability buckets.

Transaction documents permit only the listed kernel methods, exact ABI arguments and native value for createMandate; there is no arbitrary destination/selector. The signed raw transaction is saved and fsynced before broadcast. Reusing an action ID with a different intent fails. Retrying the same ID reuses the exact raw bytes/hash and existing receipt; unresolved earlier signed actions block new actions to avoid reusing their nonce. Use one private journal per signing wallet and one writer; do not transact from another wallet process concurrently. Fee fields come from the pinned RPC and support EIP-1559 or legacy networks, with a hard 100 gwei per-gas safety cap; the chosen signed bytes are immutable. If a signed transaction remains pending after fees rise, the operator first checks its hash and intent, then may explicitly retry the same action as tx ... --broadcast --replace. Replacement preserves destination, calldata, value, sender, nonce and every raw attempt; it only raises fees by at least 12.5%, never beyond the safety cap. At most one same-nonce attempt can settle. After a process crash the wallet lock deliberately remains. Stop all writers and inspect the journal plus on-chain status. If any signed transaction has no saved receipt, release only the stale process lock with the explicit recovery command, then retry that exact action; signed attempts and receipts are never erased:

node scripts/capsule.mjs journal-recover JOURNAL_DIRECTORY I_HAVE_STOPPED_ALL_CAPSULE_CLIENT_PROCESSES

This phrase-gated command removes only safe stale wallet lock/staging files, fsyncs the journal and lists every unresolved action that must be retried. Never create a new action ID to hide an uncertain earlier outcome.

The image's default account and capsule-check Node process are non-root. Legacy compose services explicitly retain their separately documented TOKN_UID/ TOKN_GID policy; this candidate does not silently change those deployments.

capsule-store.mjs CONFIG PRIVATE_DATA_DIRECTORY PORT listens only on loopback. For an authorized experiment, put it in its own bounded Docker service; expose only through a separately reviewed HTTPS proxy. The repository supplies the network-isolated check service, not an internet production deployment kit. The storage process receives a public config and its own ciphertext volume only; never mount the project root, .env, signing keys or recovery keys into it.

Defaults: 256 objects, 16 MiB ciphertext quota, four concurrent requests, 65,536-byte objects, 15-second requests. JSON/base64 metadata and filesystem overhead are additional to ciphertext quota. The process rejects bad content type/size/hash, missing accepted jobs, expired windows, conflicting bytes, corrupt stored records and quota races. It has no arbitrary URL fetching, auto-acceptance or payment key. Anyone who knows an accepted job ID can fetch its encrypted bytes; confidentiality is possession of the separate decryption key, not secrecy of an on-chain ID. Public metadata still exposes timing, sizes, amounts and addresses. Encryption cannot revoke a key/plaintext already copied.

The provider promises retention through deadline + retentionSeconds (1 hour to 30 days after deadline). Files are not automatically evicted or replenished; quota exhaustion fails closed. A timeout-refunded accepted job may remain retrievable until that promise expires. Future retention/payment bonds or garbage collection need their own reviewed protocol; do not imply they exist.

Recovery and security limits

Confirmed storage uses file fsync, immutable hard-link publication and directory fsync. Abrupt process death after acknowledgement is tested. Power-loss, filesystem/controller behavior, disk loss and backups are not thereby proven. Private POSIX directories without a hostile same-UID process are required; symlink/hardlink rejection is not an OS sandbox or general TOCTOU defence.

After stopping all store processes using the volume, the operator can run:

node scripts/capsule-store.mjs recover PRIVATE_DATA_DIRECTORY I_HAVE_STOPPED_ALL_CAPSULE_STORE_PROCESSES

Run this administrative service operation in Docker with the same narrowly scoped volume. It removes only this implementation's .pending-* staging files and service.lock, fsyncs the directory, and prints exactly what it removed. Committed capsule files and identity are never deleted or overwritten. Stale staging files block normal startup until this explicit recovery. PID-based lock stealing is not used: container PIDs can be reused. This command trusts the operator's quiescence assertion; it must not run alongside a live writer.

Recovery keys remain solely with the originating controller in this candidate. Key loss means no plaintext recovery; a chain commitment cannot reconstruct the key. Threshold recovery, key rotation/access governance, replicated availability, adversarial provider attestation and independently controlled observers remain open. The owner may revoke a signing mandate and create a new explicitly funded one; this does not rotate encryption keys or retract already learned data.

Primary-source comparison and rollout gates

EIP-712 supplies typed signatures, not nonce/replay policy. AP2 is relevant to independently authorized spending mandates; ERC-8183 has a different draft ERC-20/evaluator settlement interface. We claim neither compliance nor an interoperability gate. RFC 5116 describes authenticated encryption; it does not guarantee remote retention.

Existing systems already solve substantial parts of this problem: Filecoin PDP provides cryptographic data-possession checks, with paid storage services, and Walrus combines storage certification with Sui coordination. TOKN does not claim a capability absent from all competitors. Its new useful local integration is a bounded agent authority and native escrow path with explicitly non-authoritative recovered memory; it offers weaker storage assurance than those proof/certificate systems. A later backend integration must verify their exact proof/finality/retention assumptions instead of translating a receipt into stronger evidence by renaming it.

Before any externally funded service: independent security review and fixes, published exact network/runtime/addresses and source hashes, explicit owner and provider consent, supported finality/RPC policy, measured resource/abuse limits, recovery rehearsal, external interoperability checks and the applicable M0 transition decision. This candidate satisfies parts of the engineering tests, not M0/M1/M2/M4 completion. No existing job, genesis, wallet, validator or production secret is changed by the laboratory or release source.