Overview
A 300-line HTTP server that hands out SSH private keys and API tokens
to authorised machines and agents. IP allow-list, optional
X-API-Key header, every request audit-logged.
Zero runtime dependencies — no Express, no Redis client,
just Node's standard library.
Designed as a sidecar for Consciousness Server and Cortex. Works standalone for any system that needs a small trusted vault without pulling in HashiCorp Vault or a cloud KMS.
Why this exists
If you run a team of AI agents, each one eventually needs credentials — SSH keys to push code, API tokens to call services, bearer tokens to authenticate into your own infrastructure. Three patterns to avoid:
-
.envfiles scattered everywhere — duplicated across hosts, leaked bygit add -A, never rotated. - Hard-coded secrets in config — even in private repos, they leak the moment a fork goes public.
- Full vault (Vault / KMS / Secret Manager) — overkill for a small team; brings its own operational burden.
Consciousness Key Server is the boring middle path: one file you can audit in an afternoon, two layers of authentication, every access logged.
Install
git clone https://github.com/build-on-ai/consciousness-key-server.git
cd consciousness-key-server
cp auth/allowed-clients.example.json auth/allowed-clients.json
# edit auth/allowed-clients.json: add client IPs and per-client API keys
docker compose up -d
curl http://localhost:3040/health Default port is 3040 — part of the
Consciousness Server ecosystem reserved range
3030–3050. Change via KEY_SERVER_PORT.
Concepts
Vault layout
The on-disk layout under keys/ is deliberately simple
— ls tells you what's there. SSH private keys live at
keys/ssh/<name> (no extension); API tokens live at
keys/<service>/api-key.txt. Set
chmod 600 on every file after populating.
Authentication — two layers
- IP allow-list — bare IPs or
/24-style CIDR prefixes inauth/allowed-clients.json. Localhost (127.0.0.1/::1) is always allowed. - Optional
X-API-Keyheader — one key per client. Rotate per-client without disrupting others. If the caller sends a key, it must match an allow-listed value; if no key is sent, the IP check alone determines access.
Audit log
Every request — success or failure — appends one line to
logs/audit.log. Format is line-oriented and stable —
pipe it into your SIEM without writing a parser.
[2026-04-25T08:00:01Z] IP=10.0.0.20 ENDPOINT=/keys/ssh/git-deploy RESULT=SUCCESS size=3247
[2026-04-25T08:00:03Z] IP=10.0.0.30 ENDPOINT=/keys/ssh/git-deploy RESULT=SUCCESS size=3247
[2026-04-25T14:22:05Z] IP=10.0.0.99 ENDPOINT=/keys/ssh/git-deploy RESULT=FORBIDDEN IP_not_whitelisted
[2026-04-25T14:22:18Z] IP=10.0.0.20 ENDPOINT=/keys/ssh/../etc/passwd RESULT=REJECTED path_traversal_attempt Common use cases
Fetch an SSH key from a host
curl -s http://localhost:3040/keys/ssh/github-deploy \
-H 'X-API-Key: replace-me-with-a-long-random-string' \
> ~/.ssh/id_deploy
chmod 600 ~/.ssh/id_deploy Fetch an API token
curl -s http://localhost:3040/keys/api/openai \
-H 'X-API-Key: replace-me-with-a-long-random-string' \
| jq -r .api_key Fleet SSH key distribution
One deploy key in the vault on the primary host. Every other host
(GPU box, dev laptop, CI runner) fetches the key at boot via a
systemd oneshot or /etc/rc.local. Zero
.env files on the fleet; rotation is one operation on
the primary host. Every fetch shows up in the audit log so you know
exactly who pulled what and when.
Consciousness Server auth-token distribution
Consciousness Server can fetch its own bearer token from the vault
at startup, so you don't put AUTH_TOKEN in
.env. Rotation becomes a vault operation, not a redeploy.
Native support is planned for CS v0.2; today integrate from your
deploy script.
Per-agent credential scoping
Each agent in your fleet gets its own X-API-Key client
identity, with per-client audit log entries. "Who fetched
OPENAI_API_KEY yesterday at 3 am?" is one line in
logs/audit.log away.
API
Five endpoints. The whole surface area.
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Health + uptime |
| GET | /keys/list | List available SSH keys and API services |
| GET | /keys/ssh/:name | Get an SSH private key (text/plain response) |
| GET | /keys/api/:service | Get an API key (JSON response) |
| GET | /audit | Last 100 audit-log entries |
Threat model
Defends against
- Accidental disclosure from
git push— keys live outside the repo entirely. - Shared-secret-in-env-file leaks — rotate one vault
entry, not ten
.envfiles. - Path-traversal probes — hard-coded rejection of
..and/in key names. - Silent access — every request appends to the audit log, including denials and rejections.
Does not defend against
- A compromised host inside the IP allow-list — IP authentication only.
- Weak or leaked
X-API-Keyvalues — you set them; keep them long and random. - Lack of TLS — service speaks plain HTTP. Wrap in a reverse proxy with TLS for cross-host use.
- A malicious reader on the same filesystem as the vault — use OS permissions; the vault is not a sandbox.
For higher-trust deployments, the obvious upgrades are: terminate
TLS at a reverse proxy, run behind a private network or VPN, set
tight OS file permissions on keys/, and rotate
X-API-Key values on a schedule. The threat model is
written for low-friction LAN use; harden as the perimeter widens.