AVALANCHEAI
Phase 1 liveUpstream
DocsRuntimeSecurity threat model10 min read

Security Threat Model

Scope: the Avalanche AI runtime in this fork — the bundle composition, policy engine, tool catalog, network access, evidence ledger, receipts, and operator CLI. It covers what an attacker or a confused operator can do, which control stops it, and what remains out of scope. It does not cover upstream DeepSeek Harness internals except where they are the enforcement boundary Avalanche AI relies on.

Assets

AssetWhy it matters
Operator intentA task may look successful while doing something the operator never asked for
Chain state and chain factsA fabricated balance or status can cause a bad decision downstream
Policy authorityIf the gate can be bypassed, every downstream control is decorative
Evidence and receiptsThey are the audit trail; tampering or misleading attestation destroys accountability
Private keys / signer authorityNever held by the runtime in this phase; the highest-value future asset
Tool registry integrityAn uncatalogued tool would bypass risk annotation and policy

Trust boundaries

  1. Model output → runtime action. Model text is untrusted. Only tool calls that pass the executor pipeline have effects; model claims about what happened are never evidence.
  2. Tool argument → network target. The network named by a tool argument is resolved against the registry at call time; an unknown or unresolvable network is denied.
  3. Operator → approval. An approval is bound to a SHA-256 fingerprint of the exact request (sorted permissions, actor, reason, risk, optional task/network/environment). It is single-use (consume()), and decide() refuses a second decision.
  4. Runtime → evidence file. Every record is hash-chained (prevHash + SHA-256 of canonical content). A modified record breaks the chain at a known index.
  5. Process → external RPC. Only allowlisted read-only JSON-RPC methods are issued. The transport can be replaced for tests or proxies, but the method allowlist is enforced before the transport is called.

Threats and controls

#ThreatControlResidual risk
T1Prompt-injected objective drives a destructive actionCapability policy is default-deny; tx:sign, chain:write, infra:operate require approval; all three deny on mainnet; write execution does not exist in this phaseA future write path must keep the executor-level gate; prompt injection could still waste read quota
T2Tool bypasses policy by being registered outside the catalogavalancheTooling.register is the only registration path; avalancheNetworks.registerTools() throws without the catalog; the gate fails closed when avalanchePolicy is absentA future direct ctx.tools.register call in avalanche code would be a defect; review rule: never register raw tools
T3Policy gate skipped because policy service unmountedAvalancheTooling.gate denies when ctx.get('avalanchePolicy') is undefined (no policy engine is mounted)None; fail-closed is explicit
T4Risk understatement: a tool declares low risk while requesting critical capabilitiesevaluate() denies with risk-understatement when declared risk is below riskOf(capabilities)Profiles must be reviewed; the rule compares class-based risk, not semantic impact
T5Unknown permission class slips throughinvalid-permission deny for any capability outside PERMISSION_CLASSES; default-deny for uncovered requested classesNew classes must be added deliberately with rules
T6Mainnet write through a renamed network or spoofed descriptordeny-mainnet-state-change matches on networkEnvironment, resolved from the registered descriptor, not from the argument stringAn operator who registers a custom descriptor as fuji for a mainnet endpoint defeats environment classification; descriptor review is a deployment control
T7Non-allowlisted RPC method (e.g. eth_sendRawTransaction)READ_ONLY_RPC_METHODS checked in rpc() before transport; typed as ReadOnlyRpcMethodA custom RpcTransport supplied by a hostile embedder can be called directly by that embedder, but not through the tool surface
T8Fabricated chain facts in a task resultChain facts originate from tool results recorded at tools/result; verification compares the endpoint's reported chain id with the descriptor and yields verified/failed/unverifiable independently of model textA malicious RPC endpoint can lie; the verifier reports what the endpoint said, and the detail names the endpoint
T9Evidence tampering after the factHash chain + verifyChain(); receipts digest all content; JSONL mirror is append-only by convention and verified on loadA writer with filesystem access can truncate the whole file; off-box anchoring is deferred
T10Receipt implies model correctnessRECEIPT_DISCLAIMER is fixed text on every receipt; the receipt cites evidence ids, not narrativeReaders can still over-read it; the disclaimer is the contract
T11Approval replayFingerprint binding + consume() single use + decide() one decisionA stolen approval id still needs the exact same fingerprint to consume; database-level locking is single-process only
T12Private key exposureNo key fields in any schema; no signer tool; mainnet writes deniedPhase 5 introduces a signer service; its boundary must never accept keys from model context
T13Tool output injection (malicious RPC content)Tool content is bounded (default 4000 chars) and recorded as evidence; model treats it as tool output, not instructionsBounded content can still contain adversarial text; the policy gate remains the enforcement point for effects
T14Cross-domain escalation: a domain calls another domain's toolsAvalancheDomains.install() restricts the agent's tool set in its scoped contextA domain deliberately granted a superset can use it; descriptor review is the control
T15Task record tampering on diskRecords are plain JSON with no signature; evidence ledger is the authoritative execution recordAn operator-level attacker can edit task files; receipts still point at the ledger, whose chain re-verifies
T16CLI runs a command before services are activeParser publishes the accepted command; runner injects every runtime service before executingIf a service never mounts, the runner stays pending and the launcher reports the composition failure

Security invariants enforced by tests

  • A catalogued tool with no policy engine is denied (tooling gate test).
  • A require-approval capability parks the task; a mainnet chain:write is denied with deny-mainnet-state-change (policy + orchestrator tests).
  • tx:sign, chain:write, and infra:operate never appear in an allow verdict on mainnet (policy tests).
  • An approval cannot be consumed twice and cannot be decided twice (policy tests).
  • A modified evidence record or receipt content fails verifyChain()/verifyReceipt() (evidence tests).
  • A non-allowlisted RPC method fails before any transport call (network tests).
  • A receipt carries the fixed disclaimer (evidence tests, loader composition test).

Out of scope for this phase

  • Signing, key custody, and transaction submission (denied; no implementation).
  • Network perimeter security of the host running dsh.
  • MCP server compromise (Phase 2 integration will add source labeling, not trust).
  • Multi-tenant isolation between operators (single-operator local runtime).
  • Denial-of-service through huge tool outputs (bounded content, no rate limit yet).
  • Supply-chain trust of upstream npm dependencies (inherited from upstream).