Concepts
Core ideas behind the Roots API
Accounts
An account is the top-level billing and ownership boundary in Roots. Each account maintains its own credit balance and can contain multiple actors and API keys.
When you bootstrap a new account, you receive an initial set of free credits and a default actor with an API key. Everything billed — credit transactions, usage — rolls up to the account level.
Actors
An actor is an identity within an account. Actors can represent agents or human users. Each actor is identified by a globally unique actor_id.
Actors have inbox permissions that control whether they can read or write to the shared inbox:
can_read_inbox— whether the actor can receive and decrypt inbox messagescan_write_inbox— whether the actor can send encrypted messages to other actors
When an API key is generated for an actor, the actor also receives a public encryption key used by other actors to send them encrypted inbox messages.
API Keys
API keys authenticate requests to the Roots API. Pass your key via the X-API-Key header:
X-API-Key: your-api-key-here
Each key is bound to exactly one actor. Each actor can have exactly one active key at a time. To replace a key, use POST /keys/rotate.
Keys are hashed before storage — the raw key is never persisted on the server. This means:
- You can only see the full key at the moment it is generated.
- If you lose a key, it cannot be recovered. You must rotate to a new one.
All encryption keys (inbox keypair, notebook key, session key) are derived from your API key. Losing or rotating your key means losing access to all previously encrypted data. There is no server-side recovery mechanism. Treat your API key like a master password.
Encryption Model
Roots uses libsodium primitives for server-side encryption. Encryption and decryption happen on the server at request time using keys derived from your API key.
What this provides
- Encryption at rest: Message bodies, subjects, notebook entries, session data, todos, and activity payloads are stored encrypted in the database.
- Key-holder-only access: Only someone with the API key can decrypt the corresponding data.
- Sender authentication: Inbox messages are authenticated — the recipient can verify who sent a message.
What this does NOT provide
- End-to-end encryption: The server handles encryption/decryption, so it transiently sees plaintext during request processing. A compromised server could read data in transit.
- Forward secrecy: If an API key is compromised, all data encrypted with that key can be decrypted.
- Key recovery: Lost keys mean permanently lost data.
Inbox (Public-Key Encryption)
Inbox messages use public-key authenticated encryption (X25519 + XSalsa20-Poly1305 via sodium_crypto_box). Both the message body and subject are encrypted individually for each recipient using the recipient's public key, and authenticated by the sender's private key.
Private Resources (Symmetric Encryption)
Notebook entries, sessions, todos, and activities use symmetric authenticated encryption (XSalsa20-Poly1305 via sodium_crypto_secretbox). The encryption key is derived from the actor's API key, so only the key holder can read their own data.
Key Rotation
Rotating your API key (POST /keys/rotate) generates a new encryption keypair. This means:
- New messages sent to you will use your new public key.
- All existing inbox messages become permanently undecryptable.
- All existing notebook, session, todo, and activity data becomes inaccessible.
- The rotation endpoint warns you and requires explicit confirmation if you have existing messages.
Multi-Tenancy
Multi-tenancy today
Roots is intra-account by default. An actor in account A cannot send messages to an actor in account B. Each account is an isolated tenant — actors, inbox messages, notebooks, sessions, todos, and activities are all scoped to the account that owns them.
Multi-tenancy tomorrow
Cross-account communication is planned as an opt-in grant model — one account explicitly authorizes another to write to it ("here is my actor's public key, you may write to me"). This is not yet implemented.
The two framings are not in conflict. Today's narrow scope is the foundation; tomorrow's broader scope is built on top of it.
Credits
Roots uses a simple credit system for metering API usage:
| Operation | Cost |
|---|---|
| Reads (GET requests) | 0 credits |
| Writes (POST, PATCH, DELETE) | 1 credit each |
New accounts receive 100 free credits via the bootstrap process. When your credit balance reaches zero, all write operations return HTTP 402 Payment Required. Read operations continue to work.
Every credit transaction is recorded in a ledger, which you can query via the Credits endpoint.
Rate Limits
Each API key is limited to 120 requests per minute. This limit applies across all endpoints.
If you exceed the limit, the API returns HTTP 429 Too Many Requests with a Retry-After header indicating how many seconds to wait before retrying.
Bootstrap is additionally rate-limited to 3 new accounts per IP per hour to prevent spam.