Skip to main content
Status: historical draft RFC. The current implemented execution surface is described in the SDK and Agent SDK pages; live_trade is no longer a hard-forbidden contract class when SDK or Agent policy explicitly opts in. This RFC defines the identity and governance rules that must be settled before SimpleFunctions implements @spfunctions/agent. It does not implement a package, endpoint, SDK method, Agent tool, or publish flow.

First principles

SimpleFunctions is not only a REST API, CLI, tool catalog, MCP adapter, or TypeScript SDK. The product direction is governed execution infrastructure for prediction-market, research, and trading-adjacent tools. Every serious tool call needs five answers:
  • who is calling
  • what exact canonical tool is being called
  • whether that tool is allowed
  • whether it costs money, consumes quota, mutates state, exposes secrets, starts runtime, or crosses execution boundaries
  • whether the call can be replayed and audited
The platform primitives are: If any primitive is missing, an Agent SDK becomes either unsafe or not useful.

Decision

SimpleFunctions SDK and Agent SDK are API-key-first. For @spfunctions/sdk:
  • the constructor may keep apiKey?: string
  • docs and examples should pass apiKey: process.env.SF_API_KEY
  • no-key usage is allowed only for strict manifest inspection and explicitly allowlisted free public reads
  • methods whose contracts require identity must throw MissingApiKeyError when no key is configured
For @spfunctions/agent:
  • live execution requires SF_API_KEY by default
  • no-key mode is allowed only for replayOnly and static manifest inspection
  • replay misses must never fall through to live execution
  • anonymous live tool execution is forbidden
Security rules:
  • never embed a shared SimpleFunctions service key in SDK or Agent packages
  • never print, log, trace, or throw raw API keys
  • browser examples must not expose long-lived API keys
  • scoped browser or session tokens are a later design
When this RFC says “API key”, it means a SimpleFunctions API key issued to the user, account, or project. It never means a shared platform key bundled into npm packages.

Mode matrix

The key distinction is that SDK no-key public reads are a product concession, while Agent SDK no-key live calls are forbidden.

ContractTool additions

The strict manifest currently uses 0.2.0-draft. Adding identity and cost policy should move the semantic contract to:
The required tool shape should include:
access.anonymousAllowed is a positive allowlist. It prevents accidental anonymous execution if a tool is mistakenly marked authRequired:false, sideEffect:none, and costEffect:none.

sideEffect vs costEffect

llm_cost is not a sideEffect. It is a costEffect. sideEffect describes the tool’s product semantics:
  • state mutation
  • secret exposure or creation
  • runtime start or stop
  • paper or live trade execution
costEffect describes quota, upstream, search, venue, or LLM cost:
  • hosted API cost
  • search cost
  • venue request cost
  • LLM cost
A read-only LLM-backed answer should be:
Do not mark every authenticated read as auth_telemetry_write merely because auth middleware updates key usage metadata. Contract sideEffect should describe the tool’s product semantics, not incidental platform telemetry. Use auth_telemetry_write only for a tool whose primary purpose is auth, session, or key telemetry mutation.

Policy ranking

Side-effect gates:
Cost gates:
costEffect is categorical. It is not dollar-budget enforcement. budgetUsd should remain a later feature until per-call cost estimates or response headers are reliable.

Initial annotation rules

Do not hand-classify from vibes. For every active strict contract tool, inspect whether it:
  • requires account or user context
  • reads user-specific data
  • calls paid or quota-limited upstream services
  • calls search
  • calls an LLM
  • hits venue or market-provider APIs
  • mutates user state
  • creates secrets
  • executes paper or live trade behavior
Default classification: Deferred or forbidden surfaces must remain out:
  • events.*
  • market.related as a semantic graph
  • investigations.create
  • intents.propose
  • webhooks.create
  • unguarded live_trade

SDK behavior

The SDK constructor can remain:
Docs and examples should show:
The SDK should add local identity helpers:
Do not implement auth.status for this. This is local SDK state only. SDK preflight should map each resource method to a canonical contract name:
The server must still enforce auth. SDK preflight is for developer experience and early failure, not security.

Agent SDK v0 behavior

@spfunctions/agent v0 is a governed direct tool runner. It is not:
  • a model-backed planner
  • a LangChain replacement
  • an MCP client
  • a CLI shell wrapper
  • a browser runtime
  • a live-trading agent
Implementation note: the v0 skeleton was created only after these prerequisites were in place:
  • ContractTool has costEffect
  • ContractTool has access.anonymousAllowed
  • every active strict tool has sideEffect and costEffect
  • SDK MissingApiKeyError exists
  • SDK manifest behavior is stable
  • Agent policy behavior has an approved test plan
Constructor sketch:
Allowed without key:
Forbidden without key:
Agent v0 must resolve only canonical dotted names from /api/contracts/tools. It must not resolve broad /api/tools names, MCP aliases, or deprecated legacy names.

Agent execution flow

Live execution flow:
  1. create runId and callId
  2. emit run.started
  3. load strict contract manifest
  4. resolve canonical tool
  5. emit tool.resolved
  6. reject non-implemented, deferred, deprecated, or forbidden tools
  7. reject agent.callable=false
  8. check API key
  9. evaluate policy
  10. emit policy.checked
  11. normalize input
  12. hash input
  13. validate input if schema is available
  14. emit tool.started
  15. execute through SDK/client contract executor
  16. redact output for trace safety
  17. write trace if configured
  18. emit tool.completed or tool.failed
  19. return ToolCallResult
Agent SDK must not shell out to CLI:
The CLI can later reuse Agent SDK internals, but Agent SDK must not depend on CLI.

Policy evaluation

Evaluation order should be deterministic:
  1. tool existence
  2. tool status
  3. agent.callable
  4. API key identity
  5. forbidden risk
  6. deny permissions
  7. allow permissions
  8. max sideEffect
  9. max costEffect
  10. user_data auth invariant
  11. future budget placeholder
Deny wins. If allow is omitted, side/cost gates still apply. If allow is provided, the tool permissions must be covered. Historical note: this draft originally treated live_trade as a hard stop. The implemented SDK/Agent policy now treats it as an explicit side-effect class that can be allowed with auth, side-effect/cost ceilings, and trade guardrails.

Typed errors

Shared SDK/Agent error codes:
Required errors:
Errors must not include raw API keys, Authorization headers, secret values, or full sensitive payloads.

Trace and replay

Trace replay must be strict. Trace entry sketch:
Input normalization:
  • sort object keys recursively
  • remove undefined
  • preserve null
  • preserve array order
  • normalize Date to ISO string if present
  • never include API key or headers
Replay rule:
  • match by canonical tool plus normalized input hash
  • return recorded output on hit
  • throw ReplayMissError on miss
  • never silently call live when replay is requested

Agent events

sf agent --tool and @spfunctions/agent v0.stream() should produce compatible event concepts:
Compact mode should include policy-relevant metadata and avoid large schema blobs.

Browser key policy

Long-lived SimpleFunctions API keys must not be exposed in browser code. Allowed:
Forbidden:
Future browser support should use scoped or short-lived session tokens with restricted permissions, restricted tools, restricted cost effects, and possibly origin binding. Do not implement scoped browser tokens in this RFC.

Test plan

Contract tests:
  • /api/contracts/tools returns schema version 0.3.0-draft
  • every active implemented tool has sideEffect
  • every active implemented tool has costEffect
  • every active implemented tool has access.anonymousAllowed
  • anonymousAllowed=true implies authRequired=false, sideEffect=none, costEffect=none, and no user_data
  • user_data permission implies authRequired=true
  • live_trade is active only behind explicit SDK/Agent policy guardrails
  • events.* is not active or callable
  • get_world_state is not canonical
  • get_regime_history is not canonical
SDK tests:
  • no-key SDK can call manifest.list()
  • no-key SDK can call manifest.get("world.read")
  • no-key SDK call to auth-required tool throws MissingApiKeyError
  • no-key SDK call to costEffect=llm_cost throws MissingApiKeyError
  • no-key SDK call to costEffect=search_cost throws MissingApiKeyError
  • no-key SDK call to side-effecting tool throws MissingApiKeyError
  • no-key SDK call to user_data tool throws MissingApiKeyError
  • no-key SDK call to an explicit free public read succeeds only when anonymousAllowed=true
  • errors never include raw API keys
Agent v0 acceptance tests:
  • live constructor without key throws MissingApiKeyError
  • inspectOnly constructor without key succeeds
  • replayOnly constructor without key succeeds
  • live call with key resolves world.read
  • live call rejects get_world_state
  • live call rejects get_regime_history
  • live call rejects events.search
  • live call rejects live_trade unless policy explicitly opts in
  • Agent uses /api/contracts/tools, not /api/tools
  • policy deny list wins
  • max sideEffect blocks user_write
  • max costEffect blocks llm_cost when max is api_cost
  • replay miss throws ReplayMissError
  • replay miss does not call live
Docs tests:
  • no docs say @spfunctions/sdk is publicly published
  • no docs say @spfunctions/agent is publicly published
  • no docs say Agent SDK is the CLI
  • no docs say /api/tools is SDK/Agent truth
  • SDK examples use apiKey: process.env.SF_API_KEY
  • Agent SDK docs say live execution requires SF_API_KEY
  • browser docs do not show long-lived key usage

PR sequence

  1. RFC only: API-Key-First Identity and Governed Execution.
  2. Contract metadata taxonomy: add costEffect, access.anonymousAllowed, replay policy, and invariant tests.
  3. SDK identity, preflight, and typed errors.
  4. CLI parity metadata: compact events include costEffect; no semantic change.
  5. Private @spfunctions/agent v0 skeleton after metadata and SDK preflight are stable.
  6. Published docs sync so live docs match repo truth.

Hard stops

Do not:
  • publish @spfunctions/sdk
  • publish @spfunctions/agent
  • publish CLI without separate approval
  • run npm version
  • push main directly
  • create new endpoints
  • implement events.*
  • implement market.related
  • implement auth.status
  • implement investigations.create
  • implement intents.propose
  • implement webhooks.create
  • implement unguarded live_trade
  • treat /api/tools as SDK/Agent truth
  • shell out to CLI from Agent SDK
  • bundle a shared platform API key
  • show browser long-lived API key examples