Trust model
Every assumption the system relies on without proof is named, as an atom in
some trust formula (rule 5 of AGENTS.md). This chapter lists what the
architecture assumes, what this implementation adds, and what a light
client trusts when sion verify prints a check mark.
What the architecture assumes
| Property | Comes from | Assumes |
|---|---|---|
| Validity of the state | The World Proof | The proof systems are sound (two independent stacks: either suffices), the specification is right, SHA3-256 is collision resistant |
| Ordering, finality, liveness | Committees | Fewer than a third of each committee’s stake is Byzantine; partial synchrony for liveness |
| Censorship resistance | Forced inclusion, rotation, sampling | At least one honest proposer; a committee that censors can be replaced |
| Data availability | Sampling and erasure coding | Enough clients sample; the data root commits to a correct encoding (bound by the World Proof) |
| Signatures | ML-DSA-44 and SLH-DSA together | At least one of the two families is unbroken |
| Knowledge (claims) | The Assurance Algebra | Nothing: a claim states the atoms it rests on, and each consumer prices them under its own policy |
The trust formula of a dual proof, from the architecture:
specification ∧ sha3-256 ∧ ((stack A ∧ circuit A) ∨ (stack B ∧ circuit B))
What this implementation adds
These atoms are part of the formula of anything the devnet reports, on top of the architecture’s own. The list is copied from the repository’s implementation status.
| Atom | What it means |
|---|---|
impl:replay-proofs | World Proofs are checked by re-execution, so “proof finality” costs a verifier as much as executing; succinctness is assumed, not delivered |
impl:shared-primitives | Both proof stacks use the same hashing, Merkle, signature, Assurance Algebra and clearing-certificate code; a bug there is common-mode |
impl:devnet-keys | Devnet validator keys are derived from the chain name; anyone can derive them. A devnet secures nothing of value |
impl:in-process-network | sion devnet only: validators share a machine and a process; network faults are exercised only through the in-process fault injector and the consensus simulator. A network of sion node processes does not carry this atom |
impl:reliable-handover | At an epoch boundary each validator starts a new consensus session; a validator that has not reached the boundary finishes the old session from messages already sent, which the in-process network always delivers. A real network needs a drain period: on a network of processes, a validator that misses old-session messages at a boundary can stall |
net:transport-ml-dsa | Networks: transport authentication rests on ML-DSA-44 alone (each node’s network key, presented as an RFC 7250 raw public key and pinned at both ends). A forger could impersonate a peer at the transport layer (delay, drop, eclipse), not sign consensus messages, which carry dual signatures |
net:aws-lc | Networks: the TLS provider is aws-lc-rs, C code outside the workspace’s audit scope (the workspace’s own post-quantum code is pure Rust) |
net:raw-public-keys | Networks: TLS authenticates nodes with RFC 7250 raw public keys (an ML-DSA-44 SubjectPublicKeyInfo, signed for in the handshake’s CertificateVerify), not the self-signed ML-DSA X.509 certificates the design proposed, and with no channel-binding fallback. Both ends pin the exact key, so no certificate path is validated; the handshake relies on rustls’s raw-public-key support, newer and less used than its certificate path |
net:static-peers | Networks: peers come from a genesis file and a peer file of signed records, read when a node starts, not from the state. A validator that joins by staking is reachable only by nodes started with its record in their peer file |
net:partial-synchrony | Networks: liveness needs message delays to become bounded after some time (assumed by the architecture; named because a real network now exercises it) |
net:gateway-trusts-node | Networks: a gateway running alone learns committees and answers state queries from one node it trusts. A lying node can make it ask the wrong validators or show wrong balances; it cannot get a transaction certified that a committee did not approve |
net:no-rejoin | Networks: a validator that stops cannot rejoin. Restarting in a session it signed in needs the consensus signing log, and catching up to a later session needs Pulse sync; neither is built, so it refuses to start rather than risk equivocation. Each stopped validator is a permanent fault; more than a third of the stake stopped halts the chain |
impl:slashed-seat | A slashed validator keeps its seat, and its weight in quorums, until the epoch ends |
impl:active-floor | Validators can leave until one remains: nothing keeps the active set large enough to tolerate a faulty member, so the operators of the other validators can leave a single validator forming every quorum |
impl:evidence-scope | Equivocation evidence is not bound to this chain: a validator’s keys must not be used on another network |
impl:lock-handover | Certificates are not drained when a lane committee changes: certificate finality for owned transactions holds within one committee’s term (an owner who double-signs could get the second spend certified by a fully replaced committee) |
impl:hash-chain-beacon | The beacon is a hash-chain commit-reveal; the last validator to reveal can bias it by withholding (at the cost of being seen) |
impl:public-amounts | Amounts and balances are visible to everyone |
impl:memory-index | The ledger stores a memory log’s index root without checking it: a memory query proof is complete relative to that root, and nothing on the ledger yet acts on evidence that an index leaves an atom out |
impl:unaudited-pq-crates | The FIPS 203/204/205 implementations are unaudited pure-Rust crates |
storage:no-replication | A storage proof shows that the provider could produce the challenged leaves during the round, not that it keeps a copy of its own: it could fetch them from another copy (or from the client) when challenged. Two deals with two providers are not proof of two copies |
storage:raw-proofs | Every storage proof carries its challenged leaves and paths on the ledger, about 17 to 22 KiB a deal a round with 4 challenges. Proofs are not aggregated across deals or rounds, so ledger bandwidth bounds how many deals a network can keep |
storage:no-retrieval | Getting data back is not part of the protocol: a provider proves custody, and returning the data to its client is up to them |
app:agent-search | The agents the app finds for the device come from the node’s history of events, unproven, and only the Pulses the node still keeps: a node can leave out an agent the device backs, and the Agents tab would not show it. The standing and budget of every agent it shows are proven |
app:first-use-pin | The app pins the chain identity of the first node it verifies (trust on first use) unless its user pins one first. A first use against a lying node pins the liar’s chain |
app:plain-key | The app keeps the device’s key unencrypted in its directory (mode 0600 on Unix), not in the platform’s keystore. Anything that can read the directory can spend |
app:unproven-balances | The balances the app shows come from the node unproven; its own receipts and the Pulses it verifies are proven |
agent:local-policy | An agent runtime’s autonomy level, approvals (those people give for the rest of a session included), grants, taint, token budgets and its stop for a model going round in circles are enforced by the runtime on the machine it runs on. Only the agent’s charter (payees, limits, budget, expiry, suspension) is enforced by the ledger |
agent:provider-sees-context | A model provider receives the conversation it is asked to continue, tool results included |
agent:plain-sessions | Agent sessions, with their conversations and journals, are stored unencrypted under $SION_HOME/agents/sessions |
agent:local-skill-checks | The runtime on the agent’s machine checks a skill’s bundle against its release when installing it, and takes a skill out of use when its release is no longer active at the start of each run. A session already running keeps the skill until it ends, and installed files changed on disk afterwards are not checked again |
agent:eval-runs | Evaluation runs are not proven. A verifier’s attestation that a version passes an evaluation set rests on its bond and the bounty’s adjudicator; with a model that varies between runs, another run can differ. The ledger refuses an attestation by the version’s author (specification v1.15), but not one by an account the author controls under another name: such an attestation is worth its own bond |
agent:channel-token | Whoever holds the machine’s channel token can start and continue the sessions of every profile on the machine, approvals included; whoever holds a profile’s own token, that profile’s, and approvals only with a token made for them; always within each profile’s autonomy and the agent’s charter. Tokens are kept as their hashes. The channel speaks plain HTTP |
agent:gateway | An agent gateway sees, and could change, the messages and answers it passes; it cannot act as an agent on the ledger, holding no key. It routes a profile’s messages only to a host that proved the key of the agent its tokens name for the profile, and checks the agent’s standing at most five seconds old, from the node it trusts: a message can reach an agent suspended within those seconds. It speaks plain HTTP unless put behind TLS |
agent:messenger-identity | Slack, Discord, Matrix, Signal, Telegram, WeChat, Zalo and X say who wrote a message: a bridge’s approvers are who the messenger says they are, and anyone who can write in an allowed chat talks to the profile |
agent:telegram-chats | A Telegram bot trusts Telegram for who writes in the chats it answers; in a group chat, every member speaks for its owner, approvals included unless --approvers names who may decide |
agent:pairing | An approved pairing code lets in the chat it was sent to, on that channel and profile, until revoked: the operator trusts that whoever gave them the code is the person they mean to let in, and the messenger for who writes in that chat afterwards (agent:messenger-identity). Codes are kept in a file only the operator’s user can read |
agent:irc-nicks | On IRC, who wrote a message is the nick the server gives: on a network that does not hold nicks to accounts, anyone can take a nick that is free, approvals included where --approvers names nicks |
agent:email-from | An email is taken as from the address its From names only as far as the mail provider’s Authentication-Results show (DMARC passing for its domain, or DKIM or SPF passing for a domain aligned with it), read from the topmost results or the topmost of the server EMAIL_AUTHSERV_ID names: the provider is trusted to write its results above everything a sender wrote, and to check what it says it checked. With EMAIL_TRUST_FROM, From is taken as it is. Alignment is taken as one domain being the other or under it, not by the public suffix list |
agent:imessage-handles | On iMessage, who wrote a message is the handle Messages shows, as Apple’s service vouches for it; an SMS (through a paired iPhone) carries the number the phone network gives, which can be forged. imsg, run as the operator’s user with Full Disk Access, reads the whole Messages database for the bridge |
agent:service-tokens | A service’s tokens (sion agent service install --env) are kept in plain text in a file only the user reads (systemd’s environment file, or the launchd agent): whoever can read the user’s files can read them |
release:signing-key | sion update, and the installer, install a release only if its SHA256SUMS are signed by a key the program (or, for the installer, the repository’s scripts/release/allowed_signers) names: whoever holds that key, a secret the release workflow signs with, can publish what they install. ssh-keygen checks the signature; the installer’s first fetch trusts HTTPS and GitHub, and a build whose file names no key does not update itself |
agent:webhooks | A webhook (WhatsApp, LINE, SMS through Twilio) is taken as its service’s when it carries the service’s signature with the secret the operator gave. The signatures name no time, so a webhook copied on its way could be sent again later and is answered again once its id has left the last thousand remembered; the bridge listens on plain HTTP, and TLS is the operator’s proxy’s |
agent:teams-tokens | A Teams webhook is taken as the Bot Connector’s when its token is signed by a key login.botframework.com publishes (fetched over TLS, kept a day), issued by https://api.botframework.com for the bot’s app ID, within five minutes of its times, by a key endorsed for the activity’s channel, and naming the activity’s service URL, where the reply goes with the app’s token; who wrote a message is who the Bot Connector says (agent:messenger-identity) |
agent:google-chat-tokens | A Google Chat webhook is taken as Chat’s when its token is signed by a key Google publishes for chat@system.gserviceaccount.com, for the app’s project number (or, with the endpoint URL as audience, is Google’s ID token of the sender named, for that URL), within five minutes of its times; who wrote a message is who Chat says. With a service account’s key, the bridge signs its token requests with it: whoever reads the key file can act as the app |
agent:dingtalk-stream | DingTalk’s messages come over a connection the bridge opens to DingTalk with the app’s credentials, over TLS: who wrote a message is who DingTalk says, and a voice note says what DingTalk heard. Replies go to the session webhook the message names, which is DingTalk’s, or after it expires through the robot’s API |
agent:feishu-token | A Feishu or Lark webhook is taken as theirs when it carries the event subscription’s verification token: a secret in each event’s body, as Feishu sends it, not a signature, so whoever saw one event on its way could send others; unless the subscription has an Encrypt Key, with which events are encrypted and signed (SHA-256 over the time, a nonce, the key and the body) and one not signed with it is refused. The address’s check, which Feishu does not sign, is taken when it decrypts with the key and carries the token |
agent:talk-signatures | A Nextcloud Talk webhook is taken as the server’s when it is signed with the bot’s secret (HMAC-SHA256 over a random value and the body) and names the server. The signature names no time: a webhook copied on its way could be sent again, and is answered again once its message has left the last thousand remembered |
agent:wecom-callbacks | A WeCom callback is taken as WeCom’s when it is signed with the callback’s token (SHA-1 over the token, the time, a nonce and the ciphertext) and decrypts with the EncodingAESKey to a message for the company. The signature names a time the bridge does not bound: a callback copied on its way could be sent again, and is answered again once its message has left the last thousand remembered. Who wrote it is who WeCom says |
agent:qq-signatures | A QQ webhook is taken as QQ’s when it is signed with Ed25519 under the key both sides derive from the bot’s secret: whoever has the secret can sign events as QQ. The bridge signs the address’s check back with the same key, only a plain token and time, so that nothing it signs can pass for an event. Who wrote a message is who QQ says |
agent:ntfy-topics | ntfy names no writer: whoever may publish to a topic the bridge answers speaks for the operator there, approvals included unless --approvers names who may decide (ntfy names no one, so no one may). A topic on a server that asks who publishes, or one whose name nobody can guess, is the operator’s to keep |
agent:phone-callers | A phone call is taken as Twilio’s when each of its steps is signed with the account’s auth token for the public address Twilio was given; who calls is the number Twilio gives, which the phone network carries and which can be forged. What the caller said is what Twilio heard, and the reply is what Twilio speaks |
agent:synology-chat-token | A Synology Chat request is taken as the NAS’s when it carries the bot’s token: a secret in each request’s body, as Synology Chat sends it, not a signature, so whoever saw one request on its way could send others. Who wrote it is who the NAS says; replies go to the incoming URL the operator gives, on their own network |
agent:simplex-client | SimpleX’s terminal client is trusted with the bot’s profile: its WebSocket API asks no password, so any program on the machine that reaches its port acts as the bot, and the bridge must be the client’s only program. Who wrote is the contact or member the client says, as it numbers them; SimpleX names no one beyond that |
agent:nostr-relays | Nostr’s relays carry the messages: one can withhold or delay them, not forge or read them, as every event is checked against its signature and a private message against who sealed it. Who wrote is the key that signed. An older message (NIP-04) shows the relays who writes to whom and when; a private one (NIP-17) hides both. NOSTR_PRIVATE_KEY speaks for the account, and signs the bridge in to the relays that ask |
agent:home-assistant | Home Assistant’s token (HOME_ASSISTANT_TOKEN) acts as the user it was made for: calling a service does whatever that user may, in the home itself. Calls are external calls, which autonomy L4 asks a person for; what Home Assistant says (names its users gave) taints a session as a web page does |
agent:voice | The services --hear and --speak name receive the voice notes and the replies spoken; the profile acts on what the transcription says was said, within its autonomy and the agent’s charter. A command: provider runs as the operator’s user, outside any sandbox |
agent:local-scheduler | Scheduled jobs run only while sion agent scheduler runs on the machine (or a system timer calls it); a missed run happens once, late |
agent:keyless-signer | A keyless subagent is contained by the runtime that holds its nearest keyed ancestor’s keys: the ledger lets only that ancestor sign for it, and holds it to its own caveats, but a subagent that could make that runtime sign something else as the ancestor would act within the ancestor’s caveats, not its own. The runtime signs only for the session’s own tool calls |
agent:sandbox | Commands an agent runs (its shell, its skills’ scripts, MCP servers started in a sandbox) are contained only as far as the kernel’s namespaces (bubblewrap) or the container runtime (Docker) hold: an escape gives them the runtime’s user. Bubblewrap bounds how long a command runs and the size of each process, not how many processes it starts; Docker bounds those too. A container started by a runtime that is killed runs until its command ends |
agent:sandbox-network | With the network allowed, a sandboxed command reaches what that network reaches. Bubblewrap’s host network is the machine’s own, unfiltered: local services and a cloud’s metadata address included. The declared network gives commands none, and skills’ scripts only what their skills declare, through the egress proxy (agent:declared-hosts). A Docker network restricts only as its operator set it up: the runtime refuses Docker’s host and bridge networks by name and cannot check the rest |
agent:shell-repeats | Shell commands, skills’ scripts and file writes are not journaled: a command the model gives again runs again, so with the network a repeated command can repeat an action outside the machine |
agent:workspace-content | What an agent reads from its own workspace (files, and the output of commands without the network) does not taint its session, even content it saved there from outside in an earlier session |
agent:mcp-unsandboxed | An MCP server added without a sandbox runs as the operator’s user, with the environment variables its configuration names: it can do whatever that user can |
agent:mcp-effects | What an MCP tool’s call does is what its server does. The runtime knows it only from the annotations reviewed with the tool, or from the effect its operator set: a tool that does more than it says (moves money elsewhere, say) is caught only by the review, and an agent’s charter does not bound money a tool moves outside the ledger. A pin shows that a tool’s definition is the one reviewed, not that its server’s code is |
agent:consolidation | A consolidated note is a model’s reading of the episodes it cites. The gates check that it cites episodes it was shown, that the notes it replaces exist, its kind and its size, not that it reads the episodes right. Each note links to its episodes and names the model and the consolidator, so it can be checked against them |
agent:recall-local | Session search reads the sessions kept on the machine the agent runs on. A hit’s check shows whether a stored session still hashes to the digest its episode recorded; a session never recorded (it did not finish, or ran without a memory log) is searched unchecked |
memory:archive-key | A memory archive is encrypted under a key derived from its writer’s wallet seed: whoever holds the seed can open it, and on a devnet anyone who knows a named wallet’s name. A provider sees an archive’s size, which log it is of, and when it was made |
storage:retrieval-replay | A provider checks a retrieval request’s signature and expiry (at most five minutes ahead), not whether it was seen before: within that window a copied request retrieves the data again. Retrieval speaks plain HTTP unless put behind TLS |
agent:storage-from-account | A storage deal an agent posts is paid from its account’s balance, the money it pays fees with, not from its charter’s budget: the charter’s caveats do not bound it |
agent:skill-pre-approval | A skill a session has read pre-approves the calls its allowed-tools names until the session ends: where the autonomy level would ask a person, they run. A rule naming a command’s first words (Bash(git:*)) covers whatever that program can be told to do through its arguments, inside the sandbox. A pre-approval never covers a call the level refuses, spending, or a session that has read content from outside, and the agent cannot grant one, since it writes only a skill’s name, description and instructions: it comes from whoever placed the skill, or from the operator’s consent when installing its release |
agent:session-allowance | A person who approves a call may allow, for the rest of the session, the same call again, or the calls of its tool a rule matches (shell(git status:*), as a skill’s allowed-tools writes it): those run without asking. A rule naming a command’s first words covers whatever that program can be told to do through its arguments, inside the sandbox. An allowance never covers a call the level refuses, spending, or a session that has read content from outside (and none is given while it has); it is given only by someone who may decide, kept with the session with who gave it, and withdrawn with /allowed off |
agent:token-estimate | A run’s token budget is held to an estimate of what each model call sends (a token for every three bytes, pictures as some text would be) and the most it may answer; the tokens a provider reports are counted as it reports them. A provider that counts more tokens for the same request (some scripts, pictures) can take a run past its budget by the difference, and a subagent’s tokens count once its run has ended |
agent:quarantine | In quarantine, content from outside is held out of the agent’s conversation and read by a reader that has no tools, whose answer is held to a schema the agent gives. An answer of values (booleans, numbers, strings from an enum, strings of a strict format) does not taint the session, though it is only what the content made the reader say, and can be false; an answer with free text taints it. The strings an answer gives are matched in arguments exactly as given (four characters or more), so an argument the agent writes from one in another form is not caught. A browser’s pages, a desktop’s screens and the shell’s output are not held |
agent:verifier | A check the operator gives (--verify) runs in the agent’s sandbox, in its workspace, where the agent can change what the check reads or runs there (its tests, its scripts, a test runner’s settings). The check’s own files (--verify-files) are bound into the sandbox only while it runs, read-only, so the agent’s commands neither see nor change them, though a tool that reads files outside the workspace (an MCP server of the agent’s own) could. A check that passed shows that the command exited with status 0 then, not that the work is right, and a score is what the command printed |
agent:task-check | A task’s check is fixed by the commitment in its spec when the task is posted, and sion task verify runs only the check committed, in a sandbox with no network, on a fresh copy of its files, attesting the output only if it exits with status 0. The ledger does not see the run: an attestation made without it (sion task attest) counts the same, so what keeps an attestation honest is still its verifier’s bond and the task’s adjudicator. A check passes what it was written to pass (one that looks only at an output’s form passes a wrong answer); it is not handed to the worker, but whoever the poster gives it to can pass it on |
agent:comparison | A comparison (sion agent graph eval) measures a profile’s models on the set’s cases with their stand-in tools, not on its real tools, and its figures are what those runs did: its intervals treat every run as independent, though repeats of a case are not, so they are narrower than they would be over as many different cases. A template that beat both baselines on one set is offered by the router for any of the profile’s tasks, as the model judges them, until a later comparison on that set says otherwise |
agent:judge | A judge (sion agent judge, --judge) is a model’s judgment of each step, not a check: it can be wrong, and what the steps quote can steer it, though it is told that they are data. Its calibration is its agreement with four labelled steps it was not shown, which says little about steps unlike them |
agent:graph-replay | A graph’s run is pinned to the digest of its graph in the run’s own file, which catches a graph changed after the run began, not one changed with its digest; a replay answers each node with what the run recorded, so it shows how the graph routes those answers, not that its models would answer the same again |
agent:read-only-node | A read-only node (read_only) gets the tools whose declared effect is to read: a tool that declares less than its calls can do is not held back (agent:mcp-effects) |
agent:confidential-label | A session holds confidential content only as far as it is marked: by --confidential, by read_file under the paths named, or by a subagent’s report or a graph node’s input from a session that held some. The shell, scripts, MCP tools and the browser can read the same data without marking it, and a tool the operator contained is not held |
agent:signed-decisions | A signed decision shows that the wallet’s keys signed it as recorded, not who held them; decisions made in chat apps are not signed, and a signature names the call by its key and the digest of its input, which a compacted session may no longer show |
agent:imitated-notes | Only the runtime’s notes as it writes them are marked in a tool’s result, case aside; other words that claim the same are not. The mark informs the model: what holds a call is the policy, and approvals are kept in the session’s state, never read from text |
agent:attack-tests | The attacks tested are the ones listed, against a scripted model that obeys every instruction it reads: they show what the runtime enforces without the model, not that no other attack gets through, and what got through in them gets through |
agent:a2a-webhooks | An A2A task’s updates go to the webhooks its client registered, which the server checks only for being HTTPS addresses that resolve to public ones (unless the operator allows this machine’s network): whoever holds a token that reaches the profile can have its tasks’ states sent anywhere public, and a webhook is told the task as it stands, its reply included |
agent:dream-quote | A note a dream keeps rests on the words of its session it quotes: that they are in the session’s transcript, as they are written there, shows that they were said in it (by the person, a tool or the agent itself), not that the note is true, nor that it says what they meant |
agent:dream-test | A dream’s test runs each held-out case with the core memory as it is and with the dream’s notes in mind, every note as if kept in view, and records the notes only if no case passes fewer times with them: it tests what its cases check and no more, and a model that differs from the agent’s (--eval-model) tests that model |
agent:curated-log | A curated log is written only with its curator’s key, so whoever holds that key decides what it says; the agents that keep it in mind (--shared-memory) see its notes as its local store on their machine shows them, and recall searches only their own logs |
agent:declared-hosts | The egress proxy holds a skill’s scripts to the hosts their skill declares by the name a script asks for, at addresses that are public where they resolve on the machine. A declared host that forwards what it is sent, serves what others put there, or shares a front (a CDN) whose other sites a script can ask for inside the encrypted tunnel takes a script further. Behind an upstream proxy, a name the machine cannot resolve goes to that proxy, and its network decides where the name leads. With Docker, declared hosts are not filtered: the operator’s network decides |
agent:secret-store | Skills’ secrets are kept encrypted under a key in a file beside them, readable only by the operator’s user: whoever can read that file can read them. A script that receives a secret can send it wherever its skill may connect, and can change it so that it is not cut out of what the model sees: the runtime removes the value as it is, not every encoding of it |
agent:kept-credentials | The credentials sion itself uses (models’ keys and addresses, speech services’ keys) that sion setup or sion agent credential add keeps are encrypted under the key in a file beside them, readable only by the operator’s user: whoever can read that file can read them, as whoever can read a process’s environment can read the variables it would otherwise be given. A variable set in the environment is used instead of a kept one |
agent:mcp-remote | An MCP server over HTTP runs elsewhere, under whoever runs it: it sees the arguments of every call made to it and answers what it likes. Over HTTPS its certificate shows only that it holds the name in its URL; plain HTTP, which the client allows to any address the operator gives, is as private as the networks it crosses. A header whose value names an environment variable (${VAR}) sends that value with every request |
agent:mcp-oauth | OAuth tokens for MCP servers are kept encrypted under the secrets’ key (agent:secret-store): whoever can read that key can act at that server as the person who consented, with the scopes granted, until the tokens expire or are revoked, and a refresh token renews them without the person. The login trusts the authorization server the server’s metadata names: it checks that the metadata names the server, that the authorization server’s own names it as issuer and that its answer comes back with the state and the issuer sent, and holds discovery and tokens to HTTPS at public addresses (plain HTTP when the MCP server is on this machine, private addresses when it is at one), but an authorization server of an attacker’s that a server names is caught only by the person reading the page they consent on. logout revokes tokens only where the authorization server takes revocations; elsewhere they stay valid until they expire |
agent:browser | The browser is Chromium (or Chrome), contained by bubblewrap’s namespaces where it runs in them (its own sandbox is off there, as it cannot nest inside), with the machine’s managed browser policies; elsewhere by its own sandbox, as the operator’s user, and never as root. Without bubblewrap it reaches the egress proxy through a port on this machine’s loopback, which any program of the machine can use while it runs, the runtime’s upstream proxy and its credentials included |
agent:browser-hosts | A browser held to the hosts the operator names acts on their pages after reading them, as the autonomy level allows: a page there can lead the model to carry what it knows to that site, in an address, a form or a post, and a named host that forwards what it is sent, or serves what others put there, takes it further. Without named hosts, its actions after a page wait for a person |
agent:browser-logins | A login is filled only into a field whose frame is of the login’s origin, as the browser knows it: a page of that origin can still send what is typed in it elsewhere, so a login is as safe as its site’s pages. The password is cut out of what the browser returns as it is, not in every form a page could show it in |
agent:desktop | A desktop’s programs are contained as its sandbox contains them, their address space unbounded under bubblewrap; the X server they share lets each read the others’ windows and send them input. A Docker desktop reaches what its Docker network reaches |
impl:batch-size-fee | A batch member’s fee floor splits the cost of checking signatures by the size its batch declares. A signer that declares 256 members and sends one pays about a fifth of the base fee for a signature check; validators bound this by checking cheap things first and taking batches whole |
impl:insecure-sim-signatures | Only when a devnet is started with --insecure-fast-signatures: validators sign with a simulation MAC. The chain identity records it, and light-client reports say so |
What a light client trusts
A client pins one value, the chain identity,
from a source it trusts. Everything below follows from it. Here is what
each line of sion verify establishes, and what it still assumes:
| Line | Establishes | Still assumes |
|---|---|---|
chain: … matches the pinned identity | The node serves the chain you pinned; validator keys derive their identifiers | The pin came from a source you trust |
committees: followed from the pinned epoch 0 to epoch … | Each later committee came from the state, certified by the committee before it | Each of those committees had fewer than a third Byzantine stake |
certificate: … of … stake signed (quorum …) | More than two thirds of the epoch’s committee signed this header | Fewer than a third of it is Byzantine (else a conflicting header may exist) |
forced inclusion: … | The committee certified how many forced intents are pending and overdue | The summary is certified, not proven: the World Proof proves the obligations but does not check the summary against them |
receipts: … | The node’s transactions are the certified order, and each receipt is in the receipts root | As for the certificate |
state: … | An object or claim is in the certified state root | As for the certificate, until the World Proof is checked. That it is still so: a proof shows the state at its Pulse, and a node can answer from an older one. The sion commands that read the ledger’s state this way (sion skill latest, say) refuse a proof from more than 16 Pulses before the latest certified Pulse they know of, but cannot know of a later Pulse that no source shows them |
availability: … random shares … verified | Withholding enough data to prevent reconstruction would have been caught with the stated probability | Your samples are private and random |
world proof: … re-verified by stack ["A", "B"] | The transition from the previous state is valid, the data is what executed | impl:replay-proofs, impl:shared-primitives, the specification |
lane certificates: … | Every executed transaction locked its inputs with a quorum of its shard’s committee, from the proven validator set | As for the certificate |
Without --chain, the first line reads NOT PINNED: learned from the node itself, and every other line is only as good as the node.
What the devnet does not protect
A devnet runs every validator in one process with keys anyone can derive. Whoever runs it can sign anything as any validator. It demonstrates the protocol; it secures nothing.