Skip to content

CSA MCP Security framework mapping

The Cloud Security Alliance MCP Security Working Group publishes the only public hardening framework that addresses Model Context Protocol servers and the wider class of components that AI agents call. Its structure splits controls into four tiers — foundational, deployment, operational, advanced — and provides a vocabulary that procurement, compliance, and security teams already use.

This page maps each control area to the concrete capability inside ACT that delivers it. It is honest about what is shipped today, what is planned, and what is deliberately out of scope. For the framework itself, see the WG repository.

StatusShipped
ACT capabilityKeyless cosign signing via GitHub OIDC; source repository pinned in every artifact

Every component published under actpkg.dev/library is signed in its upstream CI with keyless cosign over GitHub’s OIDC token — no long-lived signing key exists. The signature binds the artifact’s OCI manifest digest to the workflow identity that produced it (a workflow in github.com/actpkg/<component>), and is attached to the artifact in the registry. Verify before you deploy:

Terminal window
cosign verify actpkg.dev/library/sqlite@sha256:<digest> \
--certificate-identity-regexp '^https://github\.com/actpkg/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

The component’s source repository URL is also recorded twice — as the OCI org.opencontainers.image.source annotation, and inside the act:component custom section, which the host reads without executing the component.

StatusPartial — pinned toolchains and inspectable artifacts shipped; verified reproducibility, LLM-audit and dependency scanning planned
ACT capabilityPinned build toolchains; CNCF Wasm OCI Artifact manifests via act-build push

act-build push emits a CNCF Wasm OCI Artifact conformant manifest with a config blob carrying architecture, os, layerDigests, and the component’s own exports and imports — so registry-side tooling can audit what an artifact claims to do without pulling the layer. Component builds pin their toolchains (rust-toolchain.toml, and a pinned container image for the harder language targets).

LLM-driven code audit, dependency vulnerability scanning, and SBOM publication are on the roadmap.

StatusShipped
ACT capabilitywasmtime component sandbox; declared capability imports

Components run inside a wasmtime virtual machine that enforces memory safety and instruction-level isolation. This is strictly stronger than container isolation: a wasm component cannot make a syscall that wasn’t explicitly imported through WASI. There is no kernel attack surface to escape — the component physically cannot express the operation. See Policy & sandbox for the runtime-grant side of the model.

StatusShipped
ACT capabilitywasi:http allow/deny lists; DNS-layer CIDR deny; per-redirect re-check

Every outbound HTTP request is gated by host policy along five axes: host, scheme, method, port, and DNS-resolved IP. CIDR-based deny lists block private ranges and metadata services (169.254.169.254/32) before they leave the sandbox. Each hop of a redirect chain is re-checked against the same policy — a 302 to a denied host fails mid-chain instead of silently succeeding. See wasi:http policy.

StatusShipped
ACT capabilityCredentials travel in session args, never in per-call metadata; a host credential store the agent cannot read

Credentials never live in per-call metadata, where every agent invocation would see them. A stateful component publishes the argument schema it expects via get-open-session-args-schema, receives its credentials once at session open, and hands back an opaque session id — that id is all the agent ever handles afterwards. Session args are also the one thing the audit trail never records, even under --audit-args.

Operators keep values in a host-side credential store rather than in config or environment variables: act secret set writes one, act login prompts for whatever a component declares in its [[std.credentials]] entries, and neither command will print a stored value back — there is deliberately no act secret get. See the auth design rationale.

StatusShipped, with one documented gap
ACT capabilityHost-side JSON Schema validation of tool arguments and session args, before the guest

Arguments reaching a component are composed by a language model reading a schema, which makes them untrusted input in the ordinary sense. The host validates them against the component’s own declared parameters-schema — and session args against get-open-session-args-schema — before the component is reached. A mismatch is refused, and the component never sees the call:

Terminal window
Error: std:invalid-args: arguments do not match the schema for 'query':
- at '/sql': want string, but got number

A guest-authored schema’s $ref is never fetched: the validator resolves only registered resources and none are registered, so a schema cannot become an outbound request the component never declared wasi:http for.

StatusShipped
ACT capabilityAlways-on audit trail: resolved policy, every capability decision, one record per tool call

Every run emits an audit trail on stderr. It opens with the artifact digest and the policy each capability actually resolved to, records each capability decision as it is made — with the reason — and closes each tool call with a rollup:

Terminal window
audit: actpkg.dev/library/sqlite sha256:cb7715 │ act:credentials=deny wasi:filesystem=allowlist wasi:http=deny wasi:sockets=deny
audit: ✓ allow wasi:filesystem write /data/app.sqlite mode:allowlist under /data/**
audit: ✗ deny wasi:filesystem read /dev/urandom outside ceiling mode:allowlist
audit: ● query ok 0ms args:1e91dd req:005ea7 session:sqlite_0

Two properties matter for an evidentiary log. It is redaction-aware by default: tool arguments are recorded as a digest, not a value, and session args — which is where ACT puts credentials — are never recorded at all. --audit-args swaps the digest for full values and warns that it can expose secrets. And it is not a logging level: the trail is filtered independently of RUST_LOG, so raising or silencing application logs cannot silence it. Only --no-audit, or [audit] enabled = false in the config file, turns it off; [audit] detail selects rollup (default) or full.

Guest-emitted telemetry is kept on a separate channel and is never merged into the audit trail — a component cannot write to its own audit record.

StatusShipped (delegated to OCI registry)
ACT capabilityImmutable OCI tags; content-addressable retrieval by digest

Components are referenced by actpkg.dev/library/<name>:<version> and resolvable by digest (@sha256:...). The host caches pulled components by reference at ~/.cache/act/components/, so the running artifact is always traceable to a registry-tagged version.

StatusShipped
ACT capabilityCapability ceiling (build-time declaration ∩ runtime grant)

This is ACT’s signature control. Every component declares the capabilities it requires in its act.toml ([std.capabilities."wasi:filesystem"], [std.capabilities."wasi:http"], etc.); the operator grants what they’re willing to give at run time. The effective policy is the intersection: a permissive operator cannot escalate past the component’s stated intent, and a lazy component cannot silently reach past the operator’s grants. See Policy & sandbox.

StatusShipped for physical capabilities; semantic-action consent specified, not yet implemented
ACT capabilityAsk-by-default prompting with a per-session decision cache and fail-safe deny

A capability ceiling answers what may this artifact reach at all. It does not answer should this particular action happen now — so the default policy mode is ask: the first time a component touches a granted capability, the operator is asked, and the answer is cached for the session. The prompt goes to the TTY for an interactive act call, or to the connected MCP client via MCP elicitation when running under act run --mcp. Where there is no channel to ask on, the answer is deny — a headless run cannot be silently escalated by leaving the mode at ask.

This defends against a different adversary than the ceiling does. The ceiling stops a rogue component — an artifact that does something other than what it claims — because the host intercepts the boundary and the component simply cannot reach what it wasn’t granted. Consent addresses a rogue agent: a mistaken or prompt-injected model driving a component that is behaving exactly as documented.

That second adversary has a gap the physical boundary cannot close. DROP DATABASE analytics travels over a socket the operator already granted; a prompt injection reaches a browser over an already-granted HTTP session. The host sees permitted bytes on a permitted channel. act:[email protected] closes it by inverting the initiative: the component names the class of action it is about to take (db:drop), and the host applies that class’s policy — the same grants, modes, and audit trail as any physical class. It is normatively specified and, like act:credentials, is a host import a component opts into. The host does not implement it yet; today a component that wants this enforces its declared semantic classes itself, and operators gate them with --deny db:drop.

StatusExperimental preview
ACT capabilityopenwallet component — reference implementation against Open Wallet Standard

The openwallet component implements OWS via the upstream ows-core and ows-signer crates. Distributed as a wasi:filesystem-bounded sandboxed component with declared capabilities. Marked as preview/demo, not a production wallet — see the component README. (OWS at openwallet.sh is independent from the OpenWallet Foundation at openwallet.foundation; the latter focuses on verifiable credentials.)

StatusShipped
ACT capabilityact:sessions/session-provider — open-session / close-session

Stateful components opt into act:[email protected], a three-function WIT interface that hands explicit lifecycle control to the host. The host keeps the wasm instance alive while sessions are open and closes whatever’s still open before tearing the instance down. Auth, parsed-spec caching, and live-connection state all live behind this boundary.

The framework areas marked partial above translate to concrete in-flight work:

  • Host-side signature verification — act pulls and runs without checking the cosign signature today; gating execution on verification is in-flight.
  • act:consent in the host — the semantic-action interface is specified and unimplemented; until it lands, semantic classes rest on the component enforcing its own declarations.
  • Verified reproducible builds — rebuild-and-diff in CI, so byte-identical output becomes a checked property rather than an aspiration.
  • LLM-driven code audit + dependency vulnerability scanning integrated into the actpkg.dev curation pipeline.
  • SBOM publication alongside every component artifact.
  • Capability attenuation — issuing a strict sub-policy of the granted ceiling to a specific agent session.

Engineering and procurement contact for the framework itself runs through the WG, not through ACT:

If you’re evaluating ACT against this framework for a procurement or compliance review, the act-spec repository is the normative source for every claim on this page. Issues, “this looks like what I’d want for X but Y is missing” reports, and gaps you spot in the mapping above are welcome at github.com/actcore/act-spec/discussions.