SoyaOS

Capabilities & sandbox

How Comet enforces what an Agent can and cannot do.

Every Agent in SoyaOS runs inside a Comet sandbox with a capability allowlist. Anything not declared in capabilities is denied. The model is fail-closed: the default state is “you cannot touch the outside world”, and the manifest opts back in for specific, narrow capabilities.

Why a capability model

Three pressures shaped this:

  1. Untrusted authorship. SoyaPacks come from anywhere. A pack you pull-ed yesterday should not be able to exfiltrate your data today if the author flipped malicious.
  2. Trustworthy audit. When an incident happens, you want to answer “what was this pack allowed to reach?” by reading one block in one YAML file, not crawling Go code.
  3. Reproducibility. Capability declarations double as a contract for Comet’s cache: a pack with egress: [] and determinism_tier: read-only is provably cacheable.

What gets gated

SurfaceMechanismBypass possible?
Network egressPer-host:port:protocol allowlist; resolved at run timeNo — DNS resolution itself goes through the allowlist.
Filesystem readMount-namespace + bind mounts, prefix-matchedNo — paths not on the read list are 404.
Filesystem writeSame as read; writes outside fs.write get EACCESNo.
Process execOnly the pack’s own interpreter; subprocess spawn is deniedNo.
Time accessReal time (clock_gettime) is allowed; setting time is notn/a — needed for sane logs.
Random/dev/urandom is always availablen/a.
Outbound DNSResolved through the Moon (which enforces the allowlist)No.

Egress

capabilities:
  egress:
    - host: api.openai.com
      port: 443
      protocol: https
    - host: hooks.slack.com
      port: 443
      protocol: https
  • Each entry is a triple — host + port + protocol — and all three must match for the connection to succeed.
  • Wildcards are accepted (*.openai.com) but pack lint --strict flags them.
  • IP literals (192.168.1.1) are accepted, but discouraged — they don’t survive a service migration.
  • Egress is enforced inside the Comet; even if your tool tries to bypass the manifest with a raw socket, the kernel netfilter chain set up at Comet start blocks the connection.

If your pack needs an unknown number of hosts (e.g. a web-crawling Agent), you have two honest options:

  • Use unrestricted determinism + egress: ["*"] — explicit about being a black box. Comet refuses to cache results.
  • Add a Moon-side proxy — the Moon’s HTTP proxy can be the only egress entry; the Moon then enforces its own list. Recommended for production.

Filesystem

capabilities:
  fs:
    read:
      - /workdir          # always available; explicit-is-better
      - /workdir/inputs
    write:
      - /workdir/out
  • All paths are inside the sandbox. The host filesystem is invisible — the Comet creates a fresh mount namespace at start.
  • Prefix-matched. /workdir/out/foo.json is allowed by fs.write: [/workdir/out].
  • /workdir itself is always read-only unless you list a subpath as writable.
  • Read of an undeclared path returns ENOENT, not EACCES — i.e., the file looks like it doesn’t exist. This is intentional: never leak the host’s directory structure.

Determinism tiers

TierSemanticsCached?
read-onlySame input always produces the same output (up to LLM nondeterminism).Yes, by input hash.
side-effectThe Agent makes observable changes outside the sandbox (creates a Linear issue, posts to Slack, writes to a DB).No.
unrestrictedEscape hatch. Treats the Agent as fully opaque. Implies side-effect.No.

The tier is enforced by Comet on the Agent’s behavior, not just its declaration. If an Agent declares read-only but its tools contain kind: builtin.http_post, validation fails at push time.

What happens on a violation

When the Comet detects a capability violation (network, fs, exec):

  1. The current syscall returns EACCES / ENOENT / ECONNREFUSED — the Agent code sees a normal-looking error.
  2. A capability_violation Scope event is emitted with the offending surface, requested resource, and matching manifest entry (or “no match”).
  3. The run exits with code 3.
  4. The Moon flags the pack version in the registry; subsequent runs of the same version emit a warning on each invocation until the pack is patched.

In CI:

$ soyaos pack lint . --strict
err   capabilities.egress declares `api.openai.com:443` but tools.fetch uses `api.anthropic.com:443`
1 error · 0 warnings

Tooling support

  • soyaos pack validate . — checks the manifest’s shape.
  • soyaos pack lint . — checks coherence between capabilities, tools, and examples.
  • soyaos pack lint . --strict — fails on wildcard hosts, missing examples for declared capabilities, and determinism_tier mismatches.

See SoyaPack v0 manifest for the manifest schema, and Scope events for the event envelope.

Edit this page on GitHub