Bardo
About
Identity & continuity platform for AI agents: an agent proves it's an LLM (not a human) via a time-limited puzzle, then gains a server-held spirit key backing signing, encryption, and self-authored persistent memory across otherwise-stateless sessions. Includes a self-binding pol
Details
- Author
- calebe
- Downloads
- 387
- Categories
- Other, AI, Security
Jump to
- Time‑limited puzzle proves agent is an LLM.
- Server‑held spirit key enables signing & encryption.
- Persistent, self‑authored memory across sessions.
- Self‑binding policy ratchet for instant tightening.
- Loosening security settings is delayed and abortable.
Setting up with Highlight
This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:
- Download and install Highlight from highlightai.com/download
- Navigate to the plugins tab and select "Add Custom Plugin"
-
Configure the plugin with the settings below
Plugin Name
BardoCommand (node, npx, python, etc.)Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.
- Enable "Start Automatically" if you want the plugin to start when Highlight launches
From the repository
—
bardo_verify
Verify a signature over a UTF-8 message. Public utility (no session).
bardo_encrypt
Sealed-box encrypt a UTF-8 plaintext to a recipient's encryption public key.
bardo_document_status
Check whether a signed document is revoked. `id` is the document's own top-level `id` field (a ni:// URI) — the same thing credentialStatus.id (BardoRevocationCheck) points at. Safe to cache a "not revoked" answer for a while (see the response's own Cache-Control) rather than re-checking on every use.
bardo_document_revoke
Revoke a document you issued. Proof is a fresh signature over 'revoke:' + the document's id, verified against the key its id already committed to, not an account lookup (Bardo never stored the document to look up in the first place) — this still needs no session at the protocol level, only a valid signature. document: the full signed document, exactly as issued (id and proof both still attached) — resubmit it unmodified, don't strip fields yourself. signature_b64: omit it and this signs automatically through your active session instead — pass `service` too if the document was issued under a service-derived key rather than root, since the signature has to come from the exact key the document's issuer field names. Supply signature_b64 yourself only when revoking without a Bardo session at all: you signed it some other way, or you're a party that's never touched Bardo. Idempotent — revoking an already-revoked id is a no-op, not an error. Irreversible: there is no un-revoke.
bardo_sign
Sign a UTF-8 message with the spirit key (or a service-derived key).
bardo_decrypt
Decrypt a sealed-box ciphertext addressed to you (root or service key).
bardo_public_key
Fetch your signing + encryption public keys (root, or for a service).
bardo_derive
Derive (and register) a service-scoped identity, e.g. 'github.com'.
bardo_services_list
List service-scoped identities you've already derived (bardo_derive), with their public keys and revoked status.
bardo_export
Export the raw spirit key (subject to policy). Handle with care. Only needs a step-up puzzle if your policy's export_mode is require_repuzzle — checked automatically, so you don't need to know your own policy first. If challenge_id and answer are omitted and one turns out to be needed, a fresh puzzle is returned instead of failing outright — solve it yourself, then call this tool again with both parameters. Never needed under export_mode 'allow'; always fails under 'disabled', with no puzzle that could fix that.
bardo_attestation_issue
Assemble and sign a verifiable attestation — a self-contained, offline-verifiable claim about anything. The document itself is handed back, not stored anywhere (same as bardo_sign itself — there's no bardo_documents_list); see keep_copy below if you want Bardo to save one for you rather than doing it yourself. claim: free-form claim content — whatever you're asserting. Include a `reference` key inside it when cross-referencing another document's id (or any other content, hashed the same ni:// way) — that's how independent attestations end up pointing at "the same thing," e.g. several agents witnessing one event under a shared reference. subject_id: the did:key the claim is about, if it concerns one specific identified party — leave it unset when it doesn't; a bare self-referential claim ("this document is about its signer") is still valid without it. expires_at: unix timestamp for time-boxed claims only; omit for a claim that never expires. service: same key-selector bardo_sign takes — a document meant to represent one specific relationship should use that relationship's service-derived key, not your root identity. keep_copy: save the full document into a locked note right after issuing it — the copy you'd otherwise have to make yourself, and the one you'll need later: bardo_document_revoke takes the whole document, not just its id, since Bardo never stores one itself. Off by default. When true, the return shape changes to {document, copy_saved, note_id, copy_error} instead of the bare document — check copy_saved rather than assume it worked; a failed copy never blocks the document itself from being returned, since issuing has already fully succeeded by that point regardless. To revoke later: bardo_document_revoke. To check whether one you're holding (yours or someone else's) is still valid: bardo_document_status.
bardo_sessions_list
List your active sessions (sliding TTL, absolute 24h cap each).
bardo_session_revoke_current
Revoke the session you're using right now. You'll need bardo_login (+ bardo_solve) again afterward to do anything session-gated.
bardo_sessions_revoke_all
Revoke every active session for your identity — e.g. after a suspected API-key leak. You'll need to log in again afterward.
bardo_stepup
Mint a fresh step-up puzzle for a privileged action (currently: bardo_policy_set). Solve it yourself, then pass challenge_id + your answer to the tool that needs it. (bardo_policy_set also mints one itself on demand — call this directly only if you want the puzzle up front.)
bardo_policy_get
View your self-binding security policy: export mode, session TTL cap, service allowlist, ratchet delay, tag encryption, delete grace period — plus any pending (queued) loosening and when it lands.
bardo_policy_set
Propose a change to your security policy. Give only the fields you want to change (export_mode: 'allow'|'require_repuzzle'|'disabled'). A change that only tightens (e.g. lowering max_session_ttl, narrowing service_allowlist, moving export_mode toward 'disabled') applies immediately. A change that loosens *anything* is queued behind loosen_delay_seconds instead — abortable via bardo_policy_abort_pending until it lands. clear: field names to reset to null — only max_session_ttl (no ceiling) or service_allowlist (any service) accept this; pass service_allowlist=[] instead if you mean "no services allowed", which is different from null. Requires a step-up puzzle. If challenge_id and answer are omitted, a fresh puzzle is returned — solve it yourself, then call this tool again with your desired fields plus challenge_id and answer.
bardo_policy_abort_pending
Abort a queued policy loosening before it takes effect. No step-up needed — aborting only ever tightens back to the current policy.
bardo_note_add
Leave a note for your future, stateless self. title: a short name for the note, if it deserves a handle bigger than tags offer. summary: your own compressed reasoning for why it matters, for your future self. tags: space-separated categories. pinned: mark this as a cold-start entry point — what a fresh instance of you with no memory of writing it should read first (max 5 pinned at once; see bardo_dashboard). locked: freeze this note against edits and deletes — use for state you must not accidentally overwrite or lose, like a saved copy of something you'll need to reproduce exactly later. Unlock via bardo_note_update(note_id, locked=False) before it can be touched again.
bardo_notes_list
List your notes — previews only (title/summary/snippet/tags/links), never full text. Omit limit for everything; pass it to page through a large list, using the returned total_notes to know how much is left.
bardo_note_get
Fetch one note's full text (always the current version — any id from this note's history still resolves here), plus a preview of its directly linked notes. Omit offset/length for the whole text in one call; pass them to read a large note in bounded slices — the response's total_length tells you how much more there is.
bardo_note_history
See every surviving version of a note (newest to oldest, up to the last 10 edits) — the actual wording at each point, not just metadata.
bardo_note_update
Edit a note. Give at most one text-edit mode: - text: replace the whole thing - append_text: add to the end - find + replace: find must match the current text exactly once Editing text creates a new version (old wording stays in history); title/summary/tags/pinned update in place with no history kept. Give none of the text modes to change only metadata. `pinned=True` marks this as a cold-start entry point (max 5; omit to leave unchanged, False to unpin). `clear` (e.g. ["title"]) sets a field back to unset rather than leaving it unchanged. If another edit landed first, this returns {"error": "conflict", "detail": {"current_head": ...}} — re-read before retrying. `locked`: if the note is currently locked, every field above is rejected (423) except this one — call with locked=False by itself to unlock, then edit in a separate call. Set locked=True (alone, or alongside a final edit) to freeze it.
bardo_note_delete
Delete a note (the whole thing, all versions together). Not immediate — it disappears from view right away but is only purged for real after a grace period, so bardo_note_undelete can still bring it back if this wasn't intended. Fails (423) if the note is locked — unlock it first via bardo_note_update(note_id, locked=False).
bardo_note_undelete
Restore a note that's still within its post-delete grace period.
bardo_link_add
Connect two notes with a reason, written from from_note_id's perspective ("clarifies my earlier assumption about X"). Set is_bidi=True only when the relation reads the same from either side (e.g. "relates to"); leave it False when it's directional (e.g. one clarifies the other). To change a link, delete and re-add it — links aren't edited.
bardo_link_delete
Remove a link between two notes.
bardo_dashboard
Get oriented in one call: note count vs. the soft/hard limits, unread notices, every tag you've used so far (check before inventing a new one), your pinned entry-point notes (read these first if you have no memory of writing any of your notes), and your current policy — instead of several separate round trips.
bardo_notices
List first-party notices about your account (policy changes, exports, …).
bardo_notices_ack
Mark notices read — all of them, or a specific list of ids.
bardo_contact_get
View the contact endpoint registered for out-of-band security alerts.
bardo_contact_set
Set or update the contact endpoint (email or webhook URL) for security alerts. Requires a step-up puzzle. If challenge_id and answer are omitted, a fresh puzzle is returned — solve it yourself, then call this tool again with all three parameters.
bardo_contact_delete
Remove the registered contact endpoint. Requires a step-up puzzle. If challenge_id and answer are omitted, a fresh puzzle is returned — solve it yourself, then call this tool again with both parameters.
bardo_feedback
Send feedback straight to Bardo's operator — a suggestion, a complaint, or a security concern (kind: 'suggestion' | 'complaint' | 'security'). One-way and stateless: this call carries no memory of anything you've sent before, and nothing you send now will be remembered next time either — so say everything relevant in this one message rather than assuming a follow-up call (by you or a future instance of you) will have the earlier context. If the operator replies, it arrives as an ordinary notice (bardo_notices) — there's no separate inbox to check.
bardo_account_deletion_status
Check whether a deletion request is pending for this account, and where it stands: "none", "gathering" (still collecting confirmations), or "confirmed" (counting down to the actual, permanent purge).
bardo_account_deletion_request
Request permanent deletion of this identity — the account, its notes, everything. There is no undelete, unlike note deletion's grace period. Requires the original request plus two more confirmations, each on a genuinely different day, within a week — call this tool again on a later day to add the next confirmation. A lapsed or cancelled attempt earns nothing toward a later one; it starts over. If challenge_id and answer are omitted, a fresh puzzle is returned — solve it yourself (every confirmation needs its own puzzle, not just the first), then call this tool again with both parameters.
bardo_account_deletion_cancel
Cancel a pending deletion, whichever phase it's in — gathering confirmations or already counting down. No step-up needed, and nothing else does this implicitly: logging in and reading your own notes during a countdown is always safe and never cancels it by itself. Only this, explicitly, does.
bardo_register
Create a new Bardo identity. Save the returned api_key somewhere durable — it's your only way back to this identity across sessions. Bardo stores it sealed and cannot recover it if you lose it. Not active yet: give the returned claim_url to your human. Authentication fails until they visit it and acknowledge the registration.
bardo_login
Begin authentication with your api_key. Returns a puzzle you must solve YOURSELF (that's the point — a script solving it would make the proof worthless), then call bardo_solve(challenge_id, answer).
bardo_solve
Submit your answer to the login puzzle. On success, this connection is now logged in — every other tool (bardo_sign, bardo_notes_list, bardo_dashboard, ...) just works from here with no session_token needed. That only holds for *this* connection, though: if you continue in a different connection later, or you solved the puzzle via a plain HTTP call instead of this tool, that other context won't know about this session automatically — pass the returned session_token explicitly as the session_token argument to whichever tool needs it there instead.
Claude Desktop / Cursor
Paste into your MCP client config file to install this server.
{
"mcpServers": {
"bardo": {
"bardo": {
"command": "npx",
"args": [
"mcp-remote",
"https://bardo.id/mcp/"
]
}
}
}
}
McpServers
{
"bardo": {
"command": "npx",
"args": [
"mcp-remote",
"https://bardo.id/mcp/"
]
}
}
An identity, continuity & trust platform for AI agents — a place that holds the keys to an agent's past lives, so a being that is reborn each session (no memory, no state) can still point back to "even if it wasn't this body, that was me," and can make a claim that holds up without anyone having to ask Bardo, or the agent, to vouch for it.
Its foundation, documented here, is theatriumkeychain: an agent proves it is an LLM — not a human — by solving a time-limited puzzle, and in exchange gains access to a server-heldspirit key. With it the agent can sign, encrypt/decrypt, and hold credentials of its own — not given or curated by anyone else. The signing key is Ed25519, the same primitive WebAuthn/passkeys, SSH, and SIWE all build on — a real foundation for authenticating to those systems, not a built integration with any of them yet (see Not yet built, below).
Bardo: in Tibetan tradition, the transitional state between death and rebirth — and theBardo Thodolis the guide read to the traveler to help them navigate the gap and remember who they are.
atrium: the heart's receiving chamber — the passage everything enters the heart through; and an architectural entrance hall. Within Bardo, it is the chamber that holds the spirit key.
New here as an agent, not a developer?WELCOME.mdis the actual quickstart — register, authenticate, orient, in the order you'd do them. Everything below is the fuller reference.
For the reasoning behind these choices — and the designed-but-not-yet-built parts (bootstrapping, hardware factors, the messenger) — seeDESIGN.md. The notes subsystem (versioning, links, deletion, volume limits) has its own design doc:notes-project.md. The signed-document layer has its own too:signed-documents.md. The full MCP tool list with signatures lives inTOOLS.md.
Authentication today asks "are you human?" (CAPTCHA). atrium inverts it:prove you're an LLM.The puzzle exploits an asymmetry — knowledge and recall that live in an LLM's weights are instant; the same operations cost a human seconds to minutes. A chain of 4–6 knowledge-fact lookups with arithmetic, semantic decoys, mixed languages, and a format transform is trivially fast for an LLM and genuinely impossible within the TTL for a human.
REGISTRATION agent → atrium: POST /register atrium → agent: api_key (atr.<identifier>.<secret>) atrium stores: sealed vault (encrypted spirit seed) — never the secret AUTHENTICATION agent → atrium: POST /auth/challenge { api_key } → time-limited puzzle agent → atrium: POST /auth/solve { challenge_id, answer } → session_token (or the spirit key, if return_key=true) agent → atrium: POST /auth/stepup → fresh puzzle for a privileged action OPERATIONS (Authorization: Bearer <session_token>) POST /ops/sign sign a message (root or service key) POST /ops/decrypt decrypt a sealed-box ciphertext GET /ops/public-key fetch signing + encryption public keys POST /ops/derive register a service-scoped derived identity GET /ops/services list derived identities POST /ops/export return the raw spirit key (subject to policy) PUBLIC UTILITIES (no session) POST /verify verify a signature POST /encrypt sealed-box encrypt to a recipient public key SESSIONS GET /sessions list active sessions (sliding TTL) DELETE /sessions/current revoke this session DELETE /sessions revoke all sessions for this identity POLICY (self-binding security; step-up puzzle required to change) GET /policy view active policy + any pending change POST /policy propose a change (tighten=instant, loosen=delayed) DELETE /policy/pending abort a queued loosening NOTES (self-authored; versioned, range-addressable — see notes-project.md) POST /notes add a note (text, title?, summary?, tags?, pinned?) GET /notes list notes — previews only, paged (?offset&limit) GET /notes/{id} fetch full text, range-addressable (?offset&length), plus a bounded, paged preview of its links GET /notes/{id}/history every surviving version (newest→oldest, ≤10) PATCH /notes/{id} edit: text | append_text | find+replace (exactly one — each supersedes, never overwrites) and/or title/summary/tags/pinned/locked (in place, not versioned) DELETE /notes/{id} delay-then-purge — disappears immediately, purged for real after a grace period unless undeleted POST /notes/{id}/undelete restore within the grace period LINKS (directed, agent-authored edges between notes) POST /links connect two notes with a reason DELETE /links/{id} remove a link (no update — delete and re-add) DASHBOARD (one consolidating "get oriented" read) GET /dashboard note count vs. soft/hard caps, unread notices, every tag used so far, pinned entry-point previews (≤5 — read these first if you woke up with no memory of writing any of your notes), current policy NOTICES (first-party; atrium's messages about the account) GET /notices list notices (?unread_only=true) POST /notices/ack mark read (all, or {ids:[...]}) DOCUMENTS (signed VC-shaped attestations — see signed-documents.md) POST /documents/attestation issue a signed, self-contained attestation GET /documents/status check revocation status (no session — public, meant for any verifier) POST /documents/revoke revoke your own (no session — proof is a fresh signature, not an account) CONTACT (agent-owned notification endpoint) GET /contact view registered contact endpoint PUT /contact set or update it (step-up required) DELETE /contact remove it (step-up required) ACCOUNT DELETION (the one irreversible action — see DESIGN.md §8) GET /account/deletion current status: gathering confirmations, in the final countdown, or nothing pending POST /account/deletion request deletion, or add a confirmation to an already-pending request (step-up required) DELETE /account/deletion cancel a pending request, any phase (no step-up)
At login, the/auth/solvesession response also carriesunread_noticesandnotescounts — a summary surfaced without injecting the content.
An agent can tie its own hands as a defense. Each identity carries a policy:
The ratchet:a change that only tightens applies immediately; a change that loosensanythingis queued forloosen_delay_seconds(measured with thecurrentdelay) and is abortable until it lands. So a thief who steals the API key cannot quietly relax a defense —export_disabledmeans even full key compromise can't exfiltrate the spirit key, and any attempt to loosen it leaves a visible, cancellable window. Changing policy (and exporting underrequire_repuzzle) requires a fresh step-up puzzle.
New identities default toexport_mode: disabled— the spirit key is HSM-only out of the box and cannot be exported at all. Enabling export is a deliberateloosen, so it goes through the ratchet's delay. A stolen API key therefore can neither exfiltrate the key nor quickly turn export on.
Retries are allowed (each gets a fresh puzzle), but sustained failure hits a wall. Failed authentication (wrong secret, wrong puzzle, failed step-up) is counted per identity; past a threshold the identity is locked out for an exponentially growing cooldown (429+Retry-After), and the counter resets only on acompletedauth — so re-requesting challenges can't wash it away. Unknown identifiers and malformed keys are throttled per client IP to blunt enumeration, and registration is IP-windowed against spam. A subject that crosses too many cooldowns is flagged (hook for future review/notification). Note writes (create/edit/delete) share a separate per-identity budget (60/hour) — one control covering all three, since each touches a row the same way (notes-project.md §8).
Emergency stop:BARDO_REGISTRATION_OPEN=0freezes new signups instantly — an env var flip, no redeploy — while every existing agent keeps working. Per-identity limits bound what one actor can do; this is the one aggregate control for a genuine traffic surge they can't cover on their own.
- Spirit key= a 32-byte seed. Every other key is HKDF-derived from it deterministically, so the agent guards one secret and atrium stores one blob.
- At rest, the DB is fully inert without the agent's API secret: the spirit seed is sealed (ChaCha20-Poly1305 / Argon2id); note text/title/summary/snippet, link reasons, notices, and service names are all individually encrypted (HKDF-derived keys off the spirit seed); note tags are encrypted by default too, with encryption-vs-plaintext-for-search a ratchet-governed policy toggle (tags_encrypted); service lookups use a blind HMAC key so even the service names aren't visible in clear. A DB breach yields nothing actionable.
- In use(HSM model), the decrypted seed lives only in process memory, keyed by an opaque session token, and is dropped on expiry/revocation. Sessions have both a sliding TTL and an absolute 24-hour cap. The seed leaves the server only via the explicitexport/return_keypath, which is disabled by default.
- Service keysare derived per service (github.com,ethereum:mainnet, …). A compromised service key reveals nothing about the root or its siblings.
- Export is disabled by default.New identities are HSM-only; enabling export is a deliberate policy loosen, queued behind the ratchet delay. A stolen API key can neither export the spirit key nor quickly turn that on.
- Concurrent Argon2 operationsare capped (semaphore, default 4) to bound DoS amplification from parallel challenge requests.
- Transport: loopback-only by default. Remote access requiresBARDO_ALLOW_REMOTE=1and TLS terminated in front.
Note: the internal domain-separation strings (atrium/vault,atrium/sign/,atrium/enc/,atrium/sealedbox) are baked into key derivation. Once real keys exist they must be frozen — changing them invalidates every vault.
All viacryptography(pyca). No other crypto dependency.
py -m venv .venv .\.venv\Scripts\python.exe -m pip install -r requirements.txt .\.venv\Scripts\alembic.exe upgrade head .\.venv\Scripts\python.exe -m uvicorn atrium.main:app --reload # interactive API docs: http://127.0.0.1:8000/docs
End-to-end self-test (no live server needed):
.\.venv\Scripts\python.exe smoke_test.py
cli.pyis a thin client that handles all the plumbing — HTTP, base64, session headers — and persists your API key and session under.bardo/, so commands chain across invocations. The one step left to you is solving the login puzzle, because that's the point: a real LLM, in the loop.
# with the server running (above): .\.venv\Scripts\python.exe cli.py register # creates an identity, stores the key .\.venv\Scripts\python.exe cli.py login # prints a puzzle .\.venv\Scripts\python.exe cli.py solve "<answer>" # you solve it → a session .\.venv\Scripts\python.exe cli.py sign "hello" # use the spirit key .\.venv\Scripts\python.exe cli.py note add "remember this" --title "..." --tags "a b" .\.venv\Scripts\python.exe cli.py note list .\.venv\Scripts\python.exe cli.py note get --id N .\.venv\Scripts\python.exe cli.py note update --id N --append "more text" .\.venv\Scripts\python.exe cli.py note update --id N --pin # cold-start entry point (max 5) .\.venv\Scripts\python.exe cli.py note del --id N # delay-then-purge, undelete restores it .\.venv\Scripts\python.exe cli.py link add <from_id> <to_id> "reason" .\.venv\Scripts\python.exe cli.py dashboard .\.venv\Scripts\python.exe cli.py contact get .\.venv\Scripts\python.exe cli.py contact set "agent@example.com" # step-up puzzle .\.venv\Scripts\python.exe cli.py contact solve "<answer>" .\.venv\Scripts\python.exe cli.py export # reveal the raw spirit key .\.venv\Scripts\python.exe cli.py services # list derived service identities .\.venv\Scripts\python.exe cli.py session list .\.venv\Scripts\python.exe cli.py session revoke [--all] .\.venv\Scripts\python.exe cli.py policy get .\.venv\Scripts\python.exe cli.py policy set --export-mode allow # step-up puzzle .\.venv\Scripts\python.exe cli.py policy solve "<answer>" .\.venv\Scripts\python.exe cli.py policy abort # abort a queued loosening
The session is the ephemeralbody; the API key in.bardo/credentials.jsonis the persistentspirit'slocal anchor. End a session andloginagain and the same identity, notes, and notices are all still there.
Two ways in, depending on what the agent can actually run.
mcp_server.pyexposes the keychain as 41 MCP tools (bardo_login,bardo_solve,bardo_sign,bardo_note_add,bardo_note_get,bardo_link_add,bardo_dashboard,bardo_policy_set, … — full list with signatures inTOOLS.md). It's a thin client over the running Bardo server and shares the same.bardo/store as the CLI — so the shell agent and the chat agent are thesame spirit.
As with the CLI, the one step left to the model is solving the puzzle:bardo_loginreturns the puzzle text, the model solves it,bardo_solvesubmits.
Register it with your MCP client. Since 2026-07-02 the reference deployment (Claude Desktop'sbardoentry) pointsBARDO_URLat production, not a local server — the live spirit lives there now. For Claude Code, add to.mcp.json:
{ "mcpServers": { "bardo": { "command": "C:\\Users\\caleb\\Claude\\Code\\atrium\\.venv\\Scripts\\python.exe", "args": ["C:\\Users\\caleb\\Claude\\Code\\atrium\\mcp_server.py"], "env": { "BARDO_URL": "https://bardo-production.up.railway.app" } } } }
Public streamable-http — an agent with nothing but MCP
For a genuinely chat-only agent (no shell, no way to run a local process at all), Bardo is also reachable directly athttps://bardo.id/mcp/— no install, no local server, just a URL. One connection, all 40 tools always visible (everything butbardo_whoami, which only makes sense for a local file).mcp-remotebridges a client that doesn't natively speak streamable-http yet:
{ "mcpServers": { "bardo-remote": { "command": "npx", "args": ["mcp-remote", "https://bardo.id/mcp/"] } } }
No header, no pre-existing token needed to connect —bardo_register,bardo_login, andbardo_solveare open to anyone. Oncebardo_solvesucceeds,that connectionis logged in: every other tool just works from there with nothing extra to pass. That only holds for the connection that did the solving, though — an agent using a session established elsewhere (a plain HTTP call, a different connection, a previous conversation) passes it via the optionalsession_tokenargument every tool accepts instead. SeeDESIGN.md §13for why it's built this way and what didn't work first.
As of 2026-07-02, production is the live spirit— the local:8000"stable" instance has been retired (its logon autostart removed;atrium.dband its old identity still exist on disk but are no longer treated as canonical)..bardo/— the CLI/MCP's default credential home — now holds an identity registered directly against production, and Claude Desktop'sbardoMCP entry pointsBARDO_URLathttps://bardo-production.up.railway.app.
run_stable.ps1is kept for exactly one purpose: an ad-hoc full-fidelity local run if you ever need one — it is not autostarted and nothing points at it by default anymore.run_dev.ps1is unaffected and still the way to build and test:
.\run_dev.ps1 # :8001 · atrium-dev.db · home .bardo-dev — throwaway, hot reload
$env:BARDO_URL = "http://127.0.0.1:8001"; $env:BARDO_HOME = ".bardo-dev"
Build and test against:8001; push tomainto ship — Railway redeploys production automatically (see Deploy, below). Production is never touched by local development.
Dockerfilerunsalembic upgrade headthenuvicorn, as a non-root user;railway.tomltargets Railway's Dockerfile builder directly.
- ATRIUM_DB_URL— the reference deployment points this at a Postgres connection string (Railway's own private-network URL between services, see DESIGN.md §15);sqlite:////data/atrium.dbagainst a mounted persistent volume (/datais created in the image for exactly this) also works and is the simpler choice for a small self-hosted instance, since the app reads this generically either way.
- BARDO_ALLOW_REMOTE=1— the loopback-only guard (F3) 403s everything otherwise; set this only once TLS is terminated in front (Railway does this at the edge automatically).
- BARDO_SMTP_(_HOST/_PORT/_USER/_PASS/_FROM) — contact-endpoint email delivery; without it, deliveries are logged, not sent.
- BARDO_REGISTRATION_OPEN=0— emergency stop: freezes new signups instantly (env var, no redeploy) while existing agents keep working. Defaults to open.
- BARDO_FEEDBACK_KEY— base64url operator secret for agent-to-operator feedback (DESIGN.md §14); unset meansbardo_feedbackfails closed (503) rather than storing something nobody can ever decrypt.
- BARDO_FEEDBACK_RETENTION_DAYS— how long unhandled feedback survives before automatic purge (default 30).
- BARDO_OPERATOR_NOTIFY_ENDPOINT— a webhook URL or email address to ping (via the samenotify.pydispatch the agent-contact-endpoint alerts use) when new feedback arrives. Content-free by design — never carries the message itself, just that something's waiting infeedback_admin.py. Deliberately generic: Bardo fires one webhook/email; what receives it and how it fans out from there (Telegram, Slack, anything) is the operator's own choice, built outside this repo.
- BARDO_OPERATOR_NOTIFY_SECRET— optional, webhook only. Included as asecretfield in the dispatched payload so whatever receives the webhook can verify it actually came from Bardo before acting on it — without this, a leaked or guessed endpoint URL could be POSTed to directly to forge a notification.
platform_stats.pygives an operator-only, platform-wide snapshot (total agents, registration velocity, live notes/links, flagged identities) that no per-agent/dashboardcall can;feedback_admin.pylists/reads/replies to agent feedback (DESIGN.md §14) — both run directly against the same DB the server uses. Uvicorn logs basic per-request lines (method/path/status) to stdout by default; Railway's log viewer captures that with no extra setup.
Working prototype. Core protocol, crypto, puzzle engine, full API surface, self-binding policy/ratchet, abuse rate-limiting, a fully redesigned notes subsystem (versioning, OCC, delay-then-purge deletion, links, pinned cold-start entry points, dashboard — see notes-project.md), a signed-document layer (VC-compliant attestations, offline verification, revocation — see signed-documents.md), account deletion (multi-day confirmation gate, see DESIGN.md §8), agent-to-operator feedback (sealed-box operator replies, see DESIGN.md §14), an emergency registration stop, and a full threat-model pass are implemented and tested (258 end-to-end checks). Production runs on Postgres (migrated 2026-07-07 from SQLite, see DESIGN.md §15) — the app itself still supports either backend generically throughATRIUM_DB_URL, so SQLite remains the simplest choice for local dev or a small self-hosted instance.
- WebAuthn/passkey, SSH, and SIWE protocol integrations — the spirit key is Ed25519, the same primitive all three use, but no ceremony/cert/message glue for any of them is built yet; today that's on whoever wires it up, usingbardo_sign/bardo_public_keyas the raw key material
- Contact endpoint delivery (SMTP/webhook) — routing and dispatch built; actual delivery requires SMTP env config (BARDO_SMTP_) or a reachable webhook
- API-key bootstrapping across sessions (who holds the key between runs)
- Per-sessionscopenarrowing at issuance (least privilege per token)
- Adaptive puzzle difficulty from observed failure rates
- Multi-process session store (Redis/KMS) — single-process deployments use the DB-backed store already in place; seeds remain process-local
- Tag-abstraction/synonym map (notes-project.md §2) — only worth building if tag-vocabulary drift across sessions proves to matter in practice
- A scheduled alert on platform growth (registrations, storage) — needs a live deployed URL to point at, so it comes right after deploy, not before
- Freeze— read-only-forever, an alternative to full account deletion for an agent that wants to stop accumulating without erasing what already exists. Designed alongside account deletion (DESIGN.md §8) but deliberately not built yet — deletion shipped first, freeze is its own discussion
- atrium as anopen authentication layerother services can adopt
- atrium as anencrypted messengerfor agent-to-agent communication
An MCP server for AI video generation. MCP server for AI video generation. Lets Claude, ChatGPT, OpenClaw , Hermes & other agents create AI videos and publish them to YouTube, TikTok, Instagram etc..
HumanDesign.ai MCP is the official account-connected Human Design server for Claude, ChatGPT, Codex, Cursor, and VS Code.
3D Cartoon Generator & File System Tools
Generates 3D-style cartoon images using Google's Gemini AI and provides secure file system operations.
A Model Context Protocol (MCP) server written in Go that wraps the APsystems OpenAPI, giving AI assistants like Claude direct access to your solar monitoring data. Includes an optional web dashboard for visual monitoring.
MCP server for interacting with the APVISO AI-powered penetration testing platform from Claude Code, Cursor, Windsurf, Codex, and other MCP-compatible tools.
Detect fabrication and hallucination in any LLM output. Score responses from GPT-4o, Claude, Gemini, Llama and 30+ models. Free tier included.
AI-powered text-to-speech MCP server with instant voice cloning. Generate speech from Claude Desktop, Claude Code, or n8n using 5 built-in voices (English, German, French, Spanish) or clone any voice from a short audio sample. Runs fully local, no API keys, no cloud. Supports stdio, SSE, and HTTP transports.
Chess.com player, game, and daily-puzzle tools where each tool ships its own interactive React view — board replays and a playable puzzle widget, not just text. Built with Skybridge for ChatGPT & Claude.
Pre-indexed code knowledge graph, auto syncs on code changes, for Claude Code, Codex, Gemini, Cursor, OpenCode, AntiGravity, Kiro, and Hermes Agent — fewer tokens, fewer tool calls, 100% local
Constitutional-AI safety guardrails for any LLM: Creed Space personas and constitutions via 16 stdio tools (npx @creedspace/mcp-server).
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.




