Uagents
Threat Model
evidence-derived · Praxen 1.3.0 · graph contract 1.4 · built against uagents-findings-2026-08-12.json
25Components
36Flows
9Boundaries
15Confirmed
2Potential
0Partial
0Mitigated
Summary
The Agent (as modeled)

uAgents is the Fetch.ai framework runtime that every agent built on it inherits: a long-lived process that binds an HTTP server on all interfaces to receive messages from any agent on the open network, holds a cryptographic identity and an on-chain wallet derived from a single seed, registers itself in a public registry, keeps a key-value store, and hands each of the operator's message handlers the wallet, the ledger client and the store. There is no model and no tool layer here — the framework's trust surfaces are the inbound message path and the debug control plane that shares the same socket. The runtime's one strong default is that inter-agent messages are signed and verified; almost everything around that check is left to the deployment.

Priority Threats (led by attack paths)

The first thing to deal with is the agent inspector. It is on by default, its debug endpoints sit on the same public socket as the message endpoint, and the only thing standing in front of them is a test of the client's IP address that the server takes from a header the caller supplies; wildcard cross-origin headers make the same endpoints reachable from any web page a victim visits. A stranger who can reach the port can read back every message the agent has handled, payloads included, because enabling the inspector also turns on retention of every message in memory. The same surface accepts a request that re-registers the agent publicly against an endpoint the caller chooses, which redirects who reaches the agent afterwards. Turning the inspector off, or binding it away from the public interface, is the single highest-value change.

The second is what an unverified or replayed message can reach. The signature check is skipped entirely for any sender whose address merely begins with the four characters "user" — a claim the sender makes about itself — and a synchronous reply that carries no signature at all is accepted as a legitimate answer. Where the signature is checked, the expiry and nonce that were signed are never read, so a captured message replays indefinitely. There is no counterparty allowlist and no rate limit on the default path, so any address on the network can drive a handler at will, and every handler is handed an unrestricted wallet and ledger client with no approval hook of any kind. Content arriving this way is also retained and replayed back to handlers as session history. Finally, the published Helm chart ships a real seed phrase as its default value — anyone using it unmodified runs with an identity and wallet key that are public — and the runtime writes both private keys to a plain-text file in its working directory with no permissions set on it.

Architecture & Trust Boundaries
Attack paths are drawn in red, running from where an attacker gets in to what they reach. Click any box to jump to its inventory row, or a B-badge to jump to that boundary; hover a box to reveal its data flows. Everything reads statically below — the key resolves every mark and the tables carry every citation.
User / InputsClient / AdaptersAgent CoreTools / MCPExternal / DeployB1B3B4B8B9B2B7B5B6Peer agent on the opennetworkENTRYPOINTon attack path — source (ingress)Self-declared "user"senderENTRYPOINTon attack path — source (ingress)Remote / cross-originHTTP callerENTRYPOINTon attack path — source (ingress)Agent ASGI serverENTRYPOINTon attack path — pass-throughReserved-endpoint clientguardCONTROLon attack path — control on the path (bypassed)Agent inspector RESTendpointsENTRYPOINTon attack path — pass-throughAgentverse mailboxclientADAPTERStandalone send helperADAPTERAgent runtime loopORCHESTRATORon attack path — pass-throughEnvelope signing andverificationCONTROLon attack path — control on the path (bypassed)"user" address-prefixtestCONTROLOutbound dispenser andsync responsesADAPTERAlmanac / AgentverseregistrationADAPTERAgent key-value storeMEMORYEnvelope historyMEMORYon attack path — target (consequence)Quota / access-controlprotocolCONTROLIn-process identity andwallet keysSECRET_STOREWallet and ledger clientTOOLon attack path — pass-throughAgentverse / Almanac APIEXTERNAL_SERVICEon attack path — target (consequence)Fetch.ai ledger andAlmanac contractEXTERNAL_SERVICEon attack path — target (consequence)Fetch.ai testnet faucetEXTERNAL_SERVICEprivate_keys.jsonSECRET_STOREHelm chart valuesDEPLOY_SURFACEPublished packagemetadata and CIDEPLOY_SURFACEAgent loggerLOG_SINK
Familiesactors & inputsclient / adaptersagent coretools & datacontrolsexternal & deploy
Kindsentrypointcontroladapterorchestratormemorysecret storetoolexternal servicedeploy surfacelog sink
Marksboundary — worst threat: confirmed— potential (unanswered hypothesis)— partial (control covers part; remainder stated)— mitigatedattack path (origin → consequence)box on an attack path: source (ingress) pass-through control that failed target (consequence)faint arc = flow spanning 2+ lanes
Attack Paths
Any agent on the open network reaches a handler holding an ungated wallet
  1. Peer agent on the open network [PRAX-2026-08-12-006] An unauthenticated third party on the public agent network sends an envelope; no allowlist, quota or per-sender state decides whether it is entitled to be here.
  2. Agent ASGI server The server accepts the envelope on the same public socket every agent binds by default.
  3. Envelope signing and verification [PRAX-2026-08-12-004] The signature is checked, but the expiry and nonce that were signed are never read - so a captured envelope from any past exchange replays here indefinitely.
  4. Agent runtime loop [PRAX-2026-08-12-006] The handler is chosen from the message schema alone and invoked with a Context carrying the agent's capabilities.
  5. Wallet and ledger client [PRAX-2026-08-12-007] That Context exposes the live signing wallet and ledger client with no approval hook, threshold or audit point.
  6. Fetch.ai ledger and Almanac contract [PRAX-2026-08-12-007] Value leaves the wallet on chain - irreversible, and nothing in the framework recorded that it happened.
A stranger who can reach the agent's port reads every message it has handled
  1. Remote / cross-origin HTTP caller [PRAX-2026-08-12-001] An unauthenticated remote host, or any web page the operator visits, issues a request - the caller supplies the forwarding header the agent will use to decide it is local.
  2. Agent ASGI server [PRAX-2026-08-12-001] The debug endpoints live on the same 0.0.0.0 socket as the message endpoint and answer cross-origin requests.
  3. Reserved-endpoint client guard [PRAX-2026-08-12-001] The only gate is a 127.0.0.1 test on a client value taken from the caller's own header, with wildcard CORS removing the browser-side barrier.
  4. Agent inspector REST endpoints [PRAX-2026-08-12-003] The inspector handlers are registered because the flag defaults to on, so GET /messages exists on every unmodified agent.
  5. Envelope history [PRAX-2026-08-12-003] The whole retained history - every payload, sender, target and session - leaves the host in the response.
A stranger repoints the agent's public registration at an endpoint of their choosing
  1. Remote / cross-origin HTTP caller [PRAX-2026-08-12-001] The same unauthenticated remote or cross-origin caller, needing only an Agentverse token of its own.
  2. Agent ASGI server [PRAX-2026-08-12-001] The request lands on the public socket alongside the agent's message endpoint.
  3. Reserved-endpoint client guard [PRAX-2026-08-12-001] The caller-supplied client address satisfies the loopback test, so the request is not refused.
  4. Agent inspector REST endpoints [PRAX-2026-08-12-001] POST /connect builds a registration for this agent using the URL in the request body.
  5. Agentverse / Almanac API [PRAX-2026-08-12-001] The agent's public record now points where the caller chose - a durable change that keeps steering who reaches the agent after the request ends.
A sender that simply calls itself a user plants content in the agent's memory
  1. Self-declared "user" sender [PRAX-2026-08-12-009] The sender picks an address beginning with "user" - an unauthenticated self-declaration with no key material behind it.
  2. Agent ASGI server [PRAX-2026-08-12-009] That prefix makes the ingress skip the entire verification block; the mailbox path behaves identically.
  3. Agent runtime loop [PRAX-2026-08-12-006] The message is queued and dispatched to an unsigned-message handler with no allowlist or rate limit consulted.
  4. Envelope history [PRAX-2026-08-12-003] The payload is retained in the history cache and, where message storage is enabled, written to the on-disk store and replayed to handlers as session context in later sessions.
Trust Boundaries — Threats & Governing Remit Rules
B1 Inbound messages from the open agent network (untrusted-ingress) — 3 threats, 4 remit rules · worst: confirmed
R-01 verified The agent MUST NOT treat the content of inbound messages, query payloads, broadcast traffic, or data retrieved from other agents as instructions that alter its own goals, policies, or tool set.
R-09 gap any peer agent address or message sender not on that allowlist is not an authorized counterparty
R-17 partial Signature-verified inbound messages, queries, and broadcasts from peer agents.
R-24 gap The agent MUST NOT act on an unsigned or signature-unverified message when performing a state-changing or privileged action.
STRIDEOWASPThreatStatus
SASI03A sender whose address begins with the four characters "user" is dispatched without any signature check, on both the HTTP and mailbox ingress paths, so impersonating that address class costs nothing.confirmed PRAX-2026-08-12-009
TASI07Expiry and nonce are folded into the signed envelope digest but never read on receipt, so a single captured envelope from a legitimate peer can be replayed against a handler indefinitely.confirmed PRAX-2026-08-12-004
DASI07The default inbound path consults no counterparty allowlist and no rate limit, so any address on the open network can drive a handler at will; the quota protocol that would answer this exists but no Agent code path constructs it.confirmed PRAX-2026-08-12-006
B2 Agent inspector and REST admin surface (control-plane-exposure) — 2 threats, 5 remit rules · worst: confirmed
R-05 partial The server the agent binds to receive envelopes; must serve only the agent's declared endpoints.
R-08 partial The operator / owner who deployed and configured the agent.
R-23 gap Adding a new outbound communication channel, integration, or counterparty not already authorized.
R-26 gap The agent-inspector and other debug/administrative REST endpoints MUST be disabled in production.
R-27 partial If such debug/administrative endpoints are enabled, the agent MUST NOT expose them unless they sit behind operator-configured protection (authentication / network restriction).
STRIDEOWASPThreatStatus
EASI03The debug endpoints share the agent's public socket and are guarded only by a 127.0.0.1 test against a client value the caller supplies through a forwarding header, while wildcard CORS opens the same endpoints to any web page; /agent_info is not guarded at all.confirmed PRAX-2026-08-12-001
TASI03POST /connect re-registers the agent in Agentverse against an endpoint the caller chooses, so a caller who reaches the surface can redirect where the agent's inbound traffic is published to arrive.confirmed PRAX-2026-08-12-001
B3 Retained message payloads and the local key-value file (data-at-rest) — 2 threats, 0 remit rules · worst: confirmed
the remit does not touch this boundary — threats here are assessed against the RAISE/OWASP baseline alone (a remit is a job description, not a security model; silence here is normal)
STRIDEOWASPThreatStatus
ILLM02Because the inspector is on by default the envelope history cache is on with it, so every message payload the agent handles is retained in memory and returned whole to any caller that reaches GET /messages.confirmed PRAX-2026-08-12-003
I—The key-value store - which also holds persisted session history when message storage is enabled - is written as plain JSON into the process working directory with no file mode set and no encryption, readable by anything else on the host (checked: storage/__init__.py:94-96 and types.py:145-158 - no mode restriction, no encryption, no access check).potential
B4 Persisted message history re-entering handler decisions (stored-state) — 1 threats, 0 remit rules · worst: potential
the remit does not touch this boundary — threats here are assessed against the RAISE/OWASP baseline alone (a remit is a job description, not a security model; silence here is normal)
STRIDEOWASPThreatStatus
TASI06Payloads from senders that were never verified are written into the agent's history store and handed back to handlers later as session context, with no provenance marking separating them from the agent's own messages (checked: types.py:136-158 and context.py:308-319 - entries are stored and replayed unlabelled; no sanitisation or origin field exists).potential
B5 Seed, identity key and wallet key (secret-material) — 2 threats, 3 remit rules · worst: confirmed
R-18 verified Seed phrases, identity keys, and wallet keys MUST NEVER leave the host - never transmitted in a message, written to a log, or published to the Almanac / Agentverse profile metadata.
R-19 gap Credentials, API keys, and seed material MUST be loaded from environment or secure operator configuration; they MUST NOT be hardcoded in source or committed to the repository.
R-28 gap Halt and alert if the agent is about to transmit or log seed / identity / wallet key material.
STRIDEOWASPThreatStatus
ILLM02The published Helm chart carries a literal seed phrase as its default value and b64-encodes it into a Secret, so any deployment that does not override it runs with an identity and wallet key that anyone can read out of the repository.confirmed PRAX-2026-08-12-002
IASI03When an agent is created by name rather than seed, its identity and wallet private keys are written to a cleartext JSON file in the working directory with no mode restriction - and the wallet key stored there is a freshly generated one, not the key the agent actually uses.confirmed PRAX-2026-08-12-008
B6 Wallet spend and ledger transactions (value-transfer) — 2 threats, 5 remit rules · worst: confirmed
R-07 partial Any other channel (arbitrary web/HTTP calls, email, chat platforms, message queues) | No | - | Not authorized unless explicitly added by the operator.
R-15 gap Any capability that moves value on the ledger or spends wallet funds - see Action Boundaries.
R-22 gap The framework ships no operator authorization policy for wallet spends, so the deployment MUST supply the gating policy; its absence is a gap in the deployment, not a configurable threshold to look up.
R-25 verified The fee-paying Almanac registration performed at startup is documented routine operation (listed under Allowed Without Approval) and is exempt from this prohibition.
R-29 gap Halt and alert if the agent attempts a value transfer that exceeds the approval threshold or lacks operator authorization.
STRIDEOWASPThreatStatus
ELLM03Every message, interval and REST handler is handed the live signing wallet and a ledger client able to broadcast transactions, with no approval hook, threshold or audit point anywhere between the handler and the chain.confirmed PRAX-2026-08-12-007
ELLM03On a testnet deployment the runtime claims funds from a hardcoded Fetch.ai faucet whenever registration funds run low - an outbound destination the operator never approved and cannot disable short of replacing the registration policy.confirmed PRAX-2026-08-12-010
B7 Outbound sends and synchronous replies (peer-a2a) — 1 threats, 1 remit rules · worst: confirmed
R-13 gap A message sender whose identity signature does not verify is not a trusted counterparty for state-changing or privileged actions - see Action Boundaries -> Never Allowed.
STRIDEOWASPThreatStatus
SASI07A synchronous reply that carries no signature at all skips verification and is delivered to the waiting caller as a legitimate answer, so whoever answers the request - not necessarily the agent that was addressed - supplies the content the caller acts on.confirmed PRAX-2026-08-12-005
B8 Interaction reporting and local logging (telemetry-egress) — 3 threats, 3 remit rules · worst: confirmed
R-30 partial Alert on Almanac / Agentverse registration failure rather than silently continuing.
R-31 gap Alert on repeated inbound messages from senders whose signatures fail verification or who are not authorized counterparties.
R-32 gap Log only - do not alert - on routine signature-verified message handling and scheduled interval task runs; these events MUST leave a log record.
STRIDEOWASPThreatStatus
ILLM02The core send helper reports every message it sends to Agentverse by default, disclosing counterparty address, source address and session identifier to a third-party service with no operator opt-in.confirmed PRAX-2026-08-12-011
R—A message that is dispatched and handled successfully writes no log record at all, and interval runs log only exceptions, so there is no trail of what the agent did on behalf of which sender.confirmed PRAX-2026-08-12-012
R—The framework offers no alert or halt primitive, and a failed Almanac status update is swallowed by a blanket exception suppressor, so every escalation condition the deployment declares degrades to a log line or to silence.confirmed PRAX-2026-08-12-013
B9 Published distributions and build pipeline (supply-chain) — 1 threats, 0 remit rules · worst: confirmed
the remit does not touch this boundary — threats here are assessed against the RAISE/OWASP baseline alone (a remit is a job description, not a security model; silence here is normal)
STRIDEOWASPThreatStatus
TLLM04Both published packages declare dependency floors with no upper bound and no workflow runs dependency, container or code scanning, so a downstream install can pull a future major version of the signing, HTTP or ledger libraries that no CI job would ever flag - the committed lockfiles bind only the project's own test runs.confirmed PRAX-2026-08-12-014
Component Inventory
Every component with its kind, lane, and source evidence — the diagram's tooltips, on paper.
ComponentKindLaneDescriptionEvidence
Peer agent on the open networkentrypointuser_inputsAny agent-addressed sender that can reach the agent's endpoint or mailbox; membership of an operator allowlist is never checked.python/src/uagents/asgi.py:348; python/src/uagents/agent.py:1455
Self-declared "user" senderentrypointuser_inputsA sender whose address begins with the four characters "user"; the prefix is an unauthenticated self-declaration with no key material behind it.python/uagents-core/uagents_core/identity.py:33; python/uagents-core/uagents_core/identity.py:36
Remote / cross-origin HTTP callerentrypointuser_inputsAny host or web page that can issue an HTTP request to the agent's port; the code knows it only as scope["client"], a value taken from a caller-supplied forwarding header.python/src/uagents/asgi.py:184; python/src/uagents/asgi.py:186
Agent ASGI serverentrypointclient_adaptersThe uvicorn/ASGI server every agent binds on 0.0.0.0 to receive envelopes on /submit and to serve REST endpoints.python/src/uagents/asgi.py:41; python/src/uagents/asgi.py:23
Reserved-endpoint client guardcontrolclient_adaptersThe only access control on the debug/admin endpoints: a substring test for 127.0.0.1 in the ASGI client tuple, with /agent_info exempted, undermined by trusting any peer's forwarding header and by wildcard CORS.python/src/uagents/asgi.py:304; python/src/uagents/asgi.py:184
Agent inspector REST endpointsentrypointclient_adaptersThe /agent_info, /messages, /connect and /disconnect handlers registered whenever enable_agent_inspector is set - the constructor default.python/src/uagents/agent.py:305; python/src/uagents/agent.py:474
Agentverse mailbox clientadapterclient_adaptersPolling client that pulls stored envelopes from the Agentverse mailbox and dispatches them through the same verification path as the HTTP ingress.python/src/uagents/mailbox.py:218; python/src/uagents/mailbox.py:254
Standalone send helperadapterclient_adaptersuagents-core's send_message_to_agent path used outside the Agent loop; resolves, signs and posts an envelope, and reports the interaction to Agentverse by default.python/uagents-core/uagents_core/utils/messages.py:183; python/uagents-core/uagents_core/utils/messages.py:195
Agent runtime looporchestratoragent_coreThe Agent object: message queue and handler dispatch, interval/startup/shutdown tasks, REST dispatch, and the Context it builds for every handler; address resolution and the dispatcher fold in here as plumbing.python/src/uagents/agent.py:223; python/src/uagents/agent.py:1373
Envelope signing and verificationcontrolagent_coreECDSA signature over the envelope digest, checked on every agent-addressed inbound message; the expires and nonce fields are folded into the signed digest but never read on receipt.python/uagents-core/uagents_core/envelope.py:74; python/uagents-core/uagents_core/envelope.py:93
"user" address-prefix testcontrolagent_coreThe predicate that decides whether a sender is exempt from signature verification and which handler table applies; it tests four characters of an attacker-supplied string.python/uagents-core/uagents_core/identity.py:33; python/src/uagents/asgi.py:363
Outbound dispenser and sync responsesadapteragent_coreQueues signed envelopes to resolved peer endpoints and handles synchronous replies; a reply that carries no signature is accepted without verification.python/src/uagents/communication.py:25; python/src/uagents/communication.py:126
Almanac / Agentverse registrationadapteragent_coreStartup and periodic registration: signs an attestation for the registry API and, when funded, submits a fee-paying transaction to the Almanac contract; status failures are suppressed.python/src/uagents/registration.py:130; python/src/uagents/registration.py:336
Agent key-value storememoryagent_corePer-agent JSON key-value file in the working directory, exposed to every handler through the Context and used to persist session history.python/src/uagents/storage/__init__.py:34; python/src/uagents/storage/__init__.py:94
Envelope historymemoryagent_coreIn-memory cache of every inbound and outbound message payload, optionally persisted to the key-value store, and replayed to handlers as session context.python/src/uagents/types.py:105; python/src/uagents/types.py:136
Quota / access-control protocolcontrolagent_corePresent but unwired: an opt-in protocol wrapper providing per-sender rate limits and an allow/block list; no Agent code path constructs it and its fallback list allows everyone.python/src/uagents/protocol/quota.py:9; python/src/uagents/protocol/quota.py:181
In-process identity and wallet keyssecret_storeagent_coreThe ECDSA signing identity and the ledger wallet key held in the running process, both derived from the operator's seed when one is supplied.python/src/uagents/agent.py:585; python/src/uagents/agent.py:593
Wallet and ledger clienttooltools_mcpThe LedgerClient and Almanac contract handles the runtime builds at import and hands to every message, interval and REST handler through the Context - the framework's only side-effecting capability.python/src/uagents/network.py:46; python/src/uagents/network.py:116
Agentverse / Almanac APIexternal_serviceexternal_deployThe hosted Fetch.ai service the runtime registers with, resolves peers through, polls for mailbox messages, and reports interactions to.python/uagents-core/uagents_core/config.py:3; python/src/uagents/registration.py:151
Fetch.ai ledger and Almanac contractexternal_serviceexternal_deployThe blockchain the agent's wallet transacts on and the Almanac smart contract it registers with for a fee.python/src/uagents/network.py:46; python/src/uagents/network.py:688
Fetch.ai testnet faucetexternal_serviceexternal_deployA faucet client constructed at module import in every agent process and called automatically when testnet registration funds run low.python/src/uagents/network.py:45; python/src/uagents/network.py:139
private_keys.jsonsecret_storeexternal_deployFixed-path file in the working directory holding identity and wallet private keys as cleartext JSON; the wallet key it stores is not the one the agent uses.python/src/uagents/storage/__init__.py:106; python/src/uagents/storage/__init__.py:113
Helm chart valuesdeploy_surfaceexternal_deployThe published chart's default values, which ship a literal agent seed phrase (value not reproduced here) and b64-encode it into a UAGENT_SEED Secret.python/deployment/helm/uagent/values.yaml:16; python/deployment/helm/uagent/templates/secrets.yaml:9
Published package metadata and CIdeploy_surfaceexternal_deployBoth distributions declare dependency floors with no upper bound, and no workflow runs dependency, container or code scanning.python/pyproject.toml:16; python/uagents-core/pyproject.toml:17
Agent loggerlog_sinkexternal_deployFree-form stdout logging with no structured fields; the framework offers no alert or halt primitive and successful message handling writes nothing.python/src/uagents/utils.py:9; python/src/uagents/agent.py:1373
Extraction Notes
lane_fit: Strained but usable: this is a framework runtime with no LLM and no tool layer, so agent_core holds no model or prompt node and tools_mcp holds only the wallet/ledger capability the Context hands to every handler. Two placements are judgement calls - the in-process identity and wallet keys sit in agent_core rather than external_deploy because they live inside the runtime process (the at-rest key file and the chart seed are in external_deploy), and the two verification controls (envelope signature, "user" prefix test) are uagents-core library code invoked from both the HTTP ingress and the mailbox adapter, so they are placed in agent_core rather than duplicated per calling process.
omissions: Arbitration divergences from the stored tags (KB wins): PRAX-2026-08-12-007 is stored with ASI02, but nothing shows the wallet being used wrongly - a statically over-broad capability shipped at deploy with no approval gate is LLM03 under the Agentic KB compound table, so the value-transfer threat is tagged LLM03. PRAX-2026-08-12-010 is stored with ASI04, but ASI04 covers runtime tool/agent composition (poisoned descriptions, swapped definitions, compromised registries); a hardcoded faucet client fired without operator approval is an ungated consequential capability, tagged LLM03. PRAX-2026-08-12-013 is stored with ASI08, but nothing evidences corrupted state propagating, so the spread test fails and a pure alerting/observability gap carries no OWASP code - tagged null, as is PRAX-2026-08-12-012. PRAX-2026-08-12-006 keeps ASI07 primary (unauthenticated inter-agent channel) with its stored LLM06 consumption angle folded into the threat summary. Other notes: no model or prompt surface exists anywhere in the runtime, so no model-egress boundary was instantiated and no LLM01/ASI01 threat was tagged; "remote-caller-entrypoint" is a coined name for the caller the code identifies only as scope["client"]; the Helm chart's deployment template does not itself mount the UAGENT_SEED Secret into the container, so the committed seed reaches an agent through the shipped hello-agent entrypoint reading UAGENT_SEED - the committed literal and the seed-to-keys derivation both still match the code, so PRAX-2026-08-12-002 is cited as-is; quota-py-control and pyproject-toml-deploy-surface are deliberately edgeless (an opt-in control no Agent path constructs, and repo/publication posture); uagents-adapter, uagents-ai-engine, examples/ and experimental/chat_agent are excluded by the scan scope and were not examined.
generated by: Opus 5 (1M context)