Capabilities & permissions

A function can do nothing beyond pure computation unless its deployment declares the capability — and for anything sensitive, the caller must additionally grant it. Both checks happen server-side in the host broker, against server-held state, on every single call.

The 8 capabilities

capabilityunlockspurposeneeds caller grant
AI_INFERENCEanimica.ai.infer / animica.ai.chatAI inference via the miner network, metered per tokenno
CALL_FUNCTIONanimica.call("owner/slug", …)nested execution of another published functionyes
CALL_APPanimica.call into an app’s functionslike CALL_FUNCTION, scoped to app targetsyes
READ_CHAINanimica.chain.head / balanceread-only Animica chain accessno
SPEND_ANManimica.wallet.pay / balancepay ANM from the CALLER’s balance, inside their granted capsyes
PERSIST_STATEanimica.state.get / set / deleteper-function encrypted key/value storeno
SCHEDULECloudSchedulethe function is allowed to be driven by schedulesno
HTTP_FETCHanimica.http.fetchhost-mediated outbound HTTPS with SSRF guardsyes

Two layers of consent

  1. The developer declares capabilities on the function at deploy time. The deploy-time validator infers which capabilities the source appears to use (advisory, to pre-fill the UI) — but enforcement never trusts the source: the host broker checks the declared list on every host call and refuses anything undeclared with CapabilityDenied.
  2. The caller grants the sensitive subset (SPEND_ANM, CALL_APP, CALL_FUNCTION, HTTP_FETCH) explicitly, per app/agent/function, with hard budget bounds. No grant, no spend — even if the capability is declared.
grants API
# a user authorizes an app once, with hard bounds; revocable any time
POST /api/cloud/v1/grants
{
  "subjectKind": "app", "subjectId": "…",
  "capabilities": ["SPEND_ANM"],
  "maxPerCallNanm": "1000000",
  "maxPerExecNanm": "5000000",
  "dailyCapNanm": "50000000",
  "allowedPayees": ["anim1…"]
}
DELETE /api/cloud/v1/grants?id=…   # takes effect on the very next host call

How SPEND_ANM actually moves money

  • User code never touches a key. It asks the host to pay; the host verifies everything.
  • Checks on every payment, atomically: grant exists, not revoked/expired, includes SPEND_ANM; amount within maxPerCallNanm; recipient in allowedPayees (when set); running total within maxPerExecNanm; UTC-day total within dailyCapNanm (conditional-update claim — concurrent executions cannot double-spend the last allowance); and the whole call tree's budget not exceeded.
  • The transfer itself is two balanced ledger postings (payer debit, payee credit) in one transaction — nothing is minted, nothing is lost.
Why this is real security, not advisory: the sandbox has no network, no credentials and no keys — every privileged operation must pass through the broker, and the broker authorizes against the deployment row, the grant row and the live budgets, never against anything the guest sent. Revocation is read fresh at every spend, so it takes effect immediately.

Failure codes your code will see

  • animica.CapabilityDenied — capability not declared, or the caller's grant is missing / revoked / doesn't cover it.
  • animica.BudgetExceeded — a spend/AI/call budget, quota or per-day cap would be exceeded.
  • animica.AnimicaError — any other host-side failure (upstream unavailable, bad request…).