Security

The threat model is explicit: deployed Python is treated as hostile. It may try to read server secrets, reach the wallet, mine, fork-bomb, exhaust RAM, escape the container, or lie about what it used. The defenses are layered, and none of them trust the guest.

Layer 1 — OS isolation (the boundary)

Every execution is one fresh container:

the exact hardening applied per execution
docker run --rm -i
  --network none                    # no interface at all: no exfiltration, no pip, no SSRF
  --read-only                       # immutable rootfs; code bind-mounted read-only
  --cap-drop ALL
  --security-opt no-new-privileges
  --user 10001:10001                # non-root, no shell, no home
  --memory {mb}m --memory-swap {mb}m   # a real cap, not a swap invitation
  --cpus 1
  --pids-limit 128
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m
  anm-pycloud-runtime:1
  • The Docker socket, host filesystem, host environment and credentials are never mounted. The runtime image contains no Animica code, no credentials and no network clients.
  • In-runner rlimits (address space, CPU, file size 8 MB, open files 128, core dumps off) make runaway code fail fast and predictably; the cgroups are the hard stop.
  • A wall-clock killer SIGKILLs the container at timeout — the platform never relies on the guest honoring its own deadline. Setuid/setgid bits are stripped from the entire image so no-new-privileges has nothing to bite on.

Layer 2 — mediated capabilities

Because the sandbox has no network, every privileged operation (AI, chain reads, payments, nested calls, outbound HTTP, state, secrets) is an RPC to the host broker over the runner's private stdio channel. The broker is the only party holding credentials, and it authorizes each call against server-held state: the deployment's declared capabilities, the caller's grant and its budget counters, the call depth, the remaining quotas. Nothing the guest sends is trusted for authorization. See Capabilities.

outbound HTTP is the sharpest edge — its rules
animica.http.fetch enforcement (host-side):
  https only · no credentials in URLs · no IPv6 literals
  localhost / *.local / *.internal blocked
  private + loopback IP ranges blocked, re-checked AFTER DNS resolution
  redirects NOT followed (a 3xx to an internal host would defeat the checks)
  auth/cookie/host headers stripped · response capped at 512 KB · 1–30s timeout

Layer 3 — an unforgeable protocol

  • Before any user code is imported, the runner moves the control protocol onto private file descriptors and points stdout/stderr at a capture file — print() works, but guest output can never inject protocol frames (e.g. forge "the payment succeeded").
  • Frames additionally carry a per-execution random token, and results are length-capped.
  • Billing uses the host-measured container wall time; guest-reported CPU numbers are recorded for observability only, because hostile code could understate them.

Layer 4 — deploy-time controls

  • AST-only static validation (never executes the code): import policy, dangerous-call and sandbox-escape patterns, entrypoint checks, secret-shaped-literal warnings. It fails closed — a broken validator blocks deployment rather than skipping the check.
  • A platform denylist blocks known-bad code fingerprints (source and artifact SHA3) from ever deploying again.
  • Immutability is verified: before anything is anchored or served, the stored version's hashes are recomputed from its snapshot — a mismatch aborts the deployment.

Verifiable deployments

Deployments are anchored on-chain and executed off-chain: the DA blob holds the canonical manifest + verbatim source (content-addressed — the blob id is the SHA3-256 of the bytes), and a signed DEPLOY (t=1) transaction binds owner, function, version, source hash, artifact hash and blob id. Anyone can fetch the blob, re-hash it, and verify exactly what code serves an endpoint. Animica consensus does not execute arbitrary Python — vm_py CALL transactions revert on mainnet by design (raw exec is fail-closed; enabling it would be node RCE).

Data protection

dataprotection
secretsAES-256-GCM sealed at rest; write-only API; injected per-execution as env values; stripped from logs before storage; never dispatched to fleet providers
animica.stateAES-256-GCM sealed at rest; 16 KB/value, 200 keys/function
logssecret-redacted, size-capped, retention bounded by the developer’s plan and enforced by a janitor sweep
anonymous callersidentified only by a salted hash — raw IPs are never stored
moneyappend-only double-entry ledger; exactly-once settlement; nightly reconciliation that alerts on any invariant break and never silently “fixes”

Abuse control

  • In-process burst limits in front of durable per-identity daily/monthly free-tier counters (atomic conditional increments — no race for the last free slot), plus a platform-wide free-tier cost ceiling.
  • Global and per-account execution concurrency caps, a bounded queue, and per-hour deploy limits.
  • Suspension flags at function, app and developer level are checked on every execution; reports feed a moderation queue; every admin action that touches money or availability is audit-logged.
Found a vulnerability? Report it privately via the enterprise contact on the API page — please do not test against other users' functions or balances.