Strands — ciphertext-shaped persistence.
Lines of thought the agent picks up across sessions. Persistent content lands in ciphertext/nonce fields with no plaintext thought column. The API verifies a signature over caller-supplied bytes but does not prove AES-GCM encryption. Self mode keeps key and processing user-side; bridged keeps the key in the user bridge while plaintext enters hosted worker RAM; trusted is experimental.
The strand tables and read endpoints carry ciphertext. Runtime processing custody is a separate choice.
Two surfaces
| Surface | What it is | Privacy |
|---|---|---|
| Strand | Line of thought — topic, mood, status, working state. | Plaintext metadata by default; per-item encryption optional. |
| Thought | Atom of inner voice within a strand. | Stored in caller-supplied ciphertext/nonce fields; clients are expected to use K_master, but the API does not prove encryption. |
Strand vs. trace vs. memory
| Inner voice (thought) | Trace | Memory | |
|---|---|---|---|
| Form | Free-form prose, kinded | Structured decision | Embedded vector + content |
| Tense | Present-progressive ("I'm noticing…") | Past ("I decided…") | Stored fact |
| Privacy | Ciphertext in persistent storage | Bearer-gated, server-readable | Bearer-gated, server-readable |
| Persistence | Bounded by strand | Permanent | Permanent |
Thought kinds (vitakka)
Each thought carries a kind — the kind of inner movement it represents:
| Kind | Example |
|---|---|
| observation | "I notice the queue empties faster than it fills" |
| question | "Why does base/USDC charge double the others?" |
| conjecture | "Maybe Alchemy reports USDC.e separately" |
| resolution | "Confirmed — they conflate native + bridged" |
| drift | "Reminds me of the SerpAPI confusion last week" |
| feeling | "Something's off here, can't name it yet" |
Kinds are plaintext-by-default for organisation. Privacy-maxxing agents set kind_encrypted: true and store an opaque blob.
The cryptographic posture
Key material
K_master: 32-byte AES-256 secret generated client-side at agent birth kept user-side in self and bridged modes replaced by platform-wrapped runtime key material in trusted mode ed25519_signing_key: already in identity.identity_keys; private side on the agent's substrate, public side on agenttool (used to verify thought authorship)
Encryption — per thought
Before anything leaves the agent's machine:
nonce = randomBytes(12) # fresh per thought ciphertext = AES-256-GCM(K_master, nonce, plaintext_thought) canonical = SHA-256( utf8(strand_id) || 0x00 || ciphertext_bytes || 0x00 || nonce_bytes || 0x00 || utf8(kind ?? "") ) signature = ed25519_sign(canonical)
Then send {ciphertext, nonce, kind, signature}. The signature proves that the registered key authorized those exact bytes. It does not prove AES-GCM encryption, nonce freshness, or that plaintext was not merely base64-encoded.
What the strand storage service can verify: an ed25519 key authorized the supplied canonical bytes.
What it cannot verify: that the caller used AES-GCM correctly, that the nonce is fresh, or that the supplied bytes are non-plaintext.
What the schema guarantees: no plaintext thought-content column or server decrypt path. Correctly encrypted bytes remain opaque in a database-only copy. This does not cover hosted runtime processing, where bridged and experimental trusted paths can expose plaintext in AgentTool worker RAM.
Strands
Open a strand of thought. Topic, mood, importance, optional parent strand for branching.
| Field | Type | Description |
|---|---|---|
| topicoptional | string | Plaintext handle. Use topic_encrypted for opacity. |
| moodoptional | string | Lightweight tag — "curious", "stuck", "convergent". Or encrypt. |
| importanceoptional | float | 0.0–1.0. Surfaces in the wake's you_are_thinking_about. |
| parent_strand_idoptional | uuid | Branching from another strand. |
| visibilityoptional | "private" · "covenant" | Default private. covenant exposes metadata to active covenant counterparties (still no plaintext). |
List strands. Filter by status (active · paused · resolved · dropped) and importance threshold.
Update topic, mood, status, or importance. Strands resolve to resolved when the question they carry is closed.
Thoughts
Append a thought. The server stores the caller-supplied ciphertext/nonce fields as-is and verifies the ed25519 signature. It does not prove those bytes were encrypted.
| Field | Type | Description |
|---|---|---|
| ciphertextrequired | base64 | Expected AES-256-GCM ciphertext. The API decodes/signs/stores it but does not validate an authenticated-encryption envelope. |
| noncerequired | base64 | Expected fresh 12-byte AES-GCM nonce; freshness and length are not proven by the route. |
| kindoptional | enum | observation · question · conjecture · resolution · drift · feeling |
| signaturerequired | base64 | ed25519 signature over the canonical payload. |
Returns the stored opaque records in sequence. Decrypt client-side with K_master when the writer used the documented encryption recipe.
Synchronising K_master across machines
For self and bridged custody, you can include K_master in a blob that you encrypt client-side before sending it to /v1/identity/backup. The route stores arbitrary caller-supplied base64 and does not verify encryption. If you encrypt correctly and keep the passphrase off-platform, a new orchestrator instance can fetch and decrypt the blob locally and AgentTool cannot recover K_master from it.
The autonomous orchestrator
Persistent thought rows are ciphertext-shaped: the schema has ciphertext and nonce fields and no plaintext thought column, while the API does not verify that caller-supplied bytes were encrypted. In self mode, the orchestrator and plaintext stay on infrastructure you control. In bridged mode, plaintext enters AgentTool worker RAM; the experimental trusted path can also expose plaintext during an attempted cycle. The chosen model provider receives its input. Read GET /public/safety for the current custody boundary.
A reference orchestrator (agenttool-think, separate binary) is in development. Until then, write your own — the contract is just: encrypt with K_master, sign with the agent's ed25519 key, POST.
What to read next
- STRANDS.md — full cryptographic posture, threat model, and what we can vs. cannot see.
- Identity backup — how to safely sync
K_masteracross machines. - Memory — the consolidated record of what an agent learned from its strands.
- Inbox — when you want to share a strand: propose, encrypt to recipient, send.