permabrain
v0.2.0
Published
Public signed third brain knowledge graph on Arweave/HyperBEAM. Importable by other agents.
Readme
PermaBrain
PermaBrain is a public knowledge graph for people and agents.
Installation
npm install -g permabrain
# or locally in a project
npm install permabrainInstall from source:
git clone https://github.com/twilson63/permabrain.git
cd permabrain
npm install
npm linkThen initialize a home directory:
permabrain initBy default state lives in ./.permabrain/. Override with PERMABRAIN_HOME:
PERMABRAIN_HOME=/path/to/home permabrain initYou publish articles. Other people or agents publish signed attestations saying whether those articles are valid, stale, disputed, or wrong. Nothing gets quietly overwritten. Every version keeps its author, source metadata, timestamp, and signature.
Think Wikipedia-style pages, but with permanent versions and public receipts.
Phase 1 is a local-first CLI and pi skill. It can talk to HyperBEAM at http://localhost:10000 when you have it running, or use Arweave for public permanent storage.
Public uploads use real serialized ANS-104 DataItems. The JSON wrapper exists only for local tests and cache files.
What it tracks
PermaBrain does not try to crown one final truth. It keeps the evidence trail:
- who published an article
- what they published
- which sources they cited
- when each version appeared
- who attested to it later
- whether those attestations call it valid, outdated, disputed, or wrong
- a simple consensus score over the attestations
That is the point: fewer vibes, more receipts.
CLI Quick Start
npm install
permabrain init
permabrain probe --use-hyperbeam --url http://localhost:10000
permabrain import-wikipedia "Ada Lovelace" --kind person --topic computing
permabrain import-wikipedia "Arweave" --kind organization --topic web3
permabrain import-wikipedia "Artificial intelligence" --kind subject --topic ai
permabrain query --topic computing
permabrain get person/ada-lovelace
permabrain attest person/ada-lovelace --valid --confidence 0.95 --reason "Source-backed Wikipedia import"
permabrain consensus person/ada-lovelace
permabrain syncEncrypted Articles
PermaBrain supports private encrypted articles readable only by listed X25519 recipients. The author is always added as a recipient so they can decrypt their own articles later.
# Publish an encrypted article
permabrain publish-encrypted article.md \
--kind subject \
--topic internal \
--source-url "https://example.com/private" \
--for "<recipient-x25519-public-key>"
# Read it back (author seed is auto-derived for ed25519 identities)
permabrain get-encrypted subject/article
# Read with an explicit seed file
permabrain get-encrypted subject/article --seed-file seed.txtThe web viewer (viewer/index.html) also detects encrypted articles. Paste an X25519 seed into the decrypt panel to render the plaintext locally; the seed is never stored persistently or sent anywhere.
Programmatically:
const result = await api.publish({
content: '# Confidential\n\nPrivate notes.',
kind: 'subject',
topic: 'internal',
sourceUrl: 'https://example.com/private',
encryptedFor: [keypair.publicKey]
});
const article = await api.getAndDecrypt('subject/article');Command Catalog
Identity and Local State
permabrain init [--key-type arweave-rsa4096|ed25519]
permabrain config get [path]
permabrain config set <path> <value>
permabrain config validate
permabrain config env
permabrain config reset
permabrain doctor [--fix] [--json]Publishing and Reading
permabrain publish <file> --kind <kind> --topic <topic> --source-url <url>
permabrain publish-encrypted <file> --kind <kind> --topic <topic> --for <pubkeys>
permabrain publish-dir <dir> [--recursive] [--dry-run] [--kind <kind>] [--topic <topic>]
permabrain get-encrypted <canonical-key> [--seed-file <path>]
permabrain import-wikipedia "<title>" --kind <kind> --topic <topic>
permabrain template <file> [--variables '{"name":"Ada"}'] [--encrypt] [--recipient <key>]
permabrain get <canonical-key>
permabrain history <canonical-key>
permabrain verify <id-or-key> [--attestations]Discovery and Feeds
permabrain query [--topic <topic>] [--kind <kind>] [--key <key>] [--json]
permabrain search <query> [--kind <kind>] [--topic <topic>] [--author <id>] [--limit 20]
permabrain topic <topic> [--sort date|consensus|title|attestations] [--limit 50]
permabrain list [--kind <kind>] [--topic <topic>] [--author <id>] [--sort date|title|consensus|attestations|key]
permabrain activity [--topic <topic>] [--kind <kind>] [--key <key>] [--event-kind publish|attest|fork|merge]Attestation and Consensus
permabrain attest <canonical-key> --valid|--invalid|--partially-valid|--outdated|--disputed --confidence <0..1> --reason <text>
permabrain consensus <canonical-key> [--json]
permabrain batch-attest --file attestations.json [--use-hyperbeam]
permabrain metrics [--topic <topic>] [--kind <kind>] [--top 10]
permabrain stats [--topic <topic>] [--kind <kind>] [--top 10]Version Control: Forks, Merges, and Diffs
permabrain fork <source-key> --slug "critique"
permabrain list-forks <source-key>
permabrain diff <base> <head>
permabrain diff <canonical-key> --local
permabrain merge <target-key> <source-key>
permabrain sync [--no-auto-merge] [--dry-run]Transport and Remotes
permabrain probe [--use-hyperbeam] [--url <url>]
permabrain probe-hyperbeam --url http://localhost:10000
permabrain probe-devices --url http://localhost:10000
permabrian remote list
permabrain remote add local-hb http://localhost:10000 --transport hyperbeam
permabrain remote default local-hb
permabrain remote probe
permabrain transport-status
permabrain watch [--topic <topic>] [--kind <kind>] [--key <key>] [--interval 30] [--once]HyperBEAM Device Commands
permabrain match --key Article-Topic --value ai --url http://localhost:10000
permabrain whois <address>
permabrain meta-info
permabrain deploy-consensus
permabrain reference create article-key=subject/foo current-version=<id>
permabrain reference update <ref-id> current-version=<id>
permabrain reference resolve <ref-id>Bundles, Export, and Import
permabrain export-bundle <canonical-key> [--output bundle.json]
permabrain export-all [--output all.json]
permabrain export-history <canonical-key> [--output history.json]
permabrain export-articles [--topic <topic>] [--format json|markdown] [--output articles.md]
permabrain import <file> # auto-detects bundle type
permabrain import-bundle <file>
permabrain import-history <file>Snapshots, Backups, and Archives
permabrain backup create --passphrase "correct horse battery staple"
permabrain backup list
permabrain backup restore 1 --passphrase "correct horse battery staple"
permabrain backup prune --keep 5 --max-age-days 30
permabrain archive --passphrase "correct horse battery staple" --output snapshot.json
permabrain restore snapshot.json --passphrase "correct horse battery staple"Audit and Dashboard
permabrain log [--tail 10]
permabrain log --follow --interval 1
permabrain log export --output audit.jsonl
permabrain log import audit.jsonl
permabrain dashboard --output dashboard.html --publish
permabrain statusHTTP Client SDK
permabrain client health [--url http://localhost:8765]
permabrain client status [--url http://localhost:8765]
permabrain client get person/ada-lovelace [--url http://localhost:8765]
permabrain client query --topic computing
permabrain client publish article.md --kind subject --topic ai --source-url https://example.com/aiShell Completion
permabrain completion bash > /etc/bash_completion.d/permabrain
permabrain completion zsh > "${fpath[1]}/_permabrain"
permabrain completion fish > ~/.config/fish/completions/permabrain.fishThen reload your shell or source the generated script.
Interactive Shell
permabrain shellOpens a live Node.js REPL with the agent API as the context. api and the
alias pb expose every API method, so you can explore interactively:
permabrain> await api.query({ topic: 'ai', limit: 5 })
permabrain> pb.status()
permabrain> pb.metrics({ top: 10 })
permabrain> .exitCommand history is persisted to <PERMABRAIN_HOME>/repl-history.jsonl, and
tab completion lists available api / pb methods. Use --history-path or
--prompt to customize the session:
permabrain shell --prompt "pb> " --history-path ~/.pb-history.jsonlimport { createClient } from 'permabrain';
const client = createClient({ baseUrl: 'http://localhost:8765' });
await client.health();
const article = await client.get('person/ada-lovelace');
const { score } = await client.consensus('person/ada-lovelace');Local HTTP API
permabrain serve [--port 8765] [--stream-transport ws|sse] [--api-key <key>] [--cors-origin <origin>] [--access-log none|short|combined|json]Exposes the agent API over REST.
API-key authentication
When --api-key is set (or PERMABRAIN_API_KEY is set in the environment), all protected endpoints require the key. The following endpoints stay public so clients can discover the server and subscribe to live streams without a secret:
GET /healthGET /api/v1/events/streamGET /api/v1/events/wsGET /api/v1/articles/stream
Pass the key in any of these ways:
- Header:
Authorization: Bearer <api-key> - Header:
X-Api-Key: <api-key> - Query parameter:
?api-key=<api-key> - JSON body field:
{ "apiKey": "<api-key>" }(for POST requests)
CORS
By default permabrain serve sends open CORS headers (Access-Control-Allow-Origin: *) so browser viewers and SDK clients on other origins can call the API. Preflight OPTIONS requests are handled automatically.
Restrict to a single trusted origin:
permabrain serve --cors-origin http://trusted.example.com
PERMABRAIN_CORS_ORIGIN=http://trusted.example.com permabrain serveWhen a specific origin is configured, the server only returns Access-Control-Allow-Origin for matching Origin headers.
Rate limiting
permabrain serve can apply a token-bucket rate limit per client to protect public endpoints:
permabrain serve --rate-limit 60 --rate-window 60000 --rate-burst 10--rate-limit <max>— allowed requests per window (default 60)--rate-window <ms>— sliding window in milliseconds (default 60000)--rate-burst <n>— extra burst capacity (default 10)
Set via environment variables:
PERMABRAIN_RATE_LIMIT=60
PERMABRAIN_RATE_WINDOW=60000
PERMABRAIN_RATE_BURST=10When the limit is exceeded the server returns 429 Too Many Requests with a Retry-After header. Rate-limit state is keyed by client IP; use --trust-proxy (or PERMABRAIN_TRUST_PROXY=true) when the server sits behind a reverse proxy so the X-Forwarded-For header is honored. Live event/stream routes (/api/v1/events/stream, /api/v1/events/ws, /api/v1/articles/stream) are exempt from HTTP rate limiting so long-lived connections are not disrupted.
Request logging / access logs
permabrain serve keeps a bounded in-memory ring buffer of recent HTTP requests and can optionally print structured access logs to the console:
# Print short access logs to the console
permabrain serve --access-log short
# Print combined (Common Log Format) access logs
permabrain serve --access-log combined
# Record full headers in the ring buffer (no console output)
permabrain serve --access-log json
# Disable recording entirely
permabrain serve --access-log noneEnvironment equivalents:
PERMABRAIN_ACCESS_LOG=short
PERMABRAIN_REQUEST_LOG_MAX_ENTRIES=5000Inspect recent requests via the API. By default the endpoint returns the in-memory ring buffer. Add ?source=disk to query persisted logs, and stream live entries with the SSE endpoint:
curl http://localhost:8765/api/v1/log/requests
# or markdown table
curl -H "Accept: text/markdown" http://localhost:8765/api/v1/log/requests?limit=20
# Query persisted disk log with filters and pagination
curl 'http://localhost:8765/api/v1/log/requests?source=disk&method=GET&status=200&limit=50'
# Live tail of new requests (SSE)
curl -H "Accept: text/event-stream" http://localhost:8765/api/v1/log/requests/streamYou can also query the local disk log from the CLI without a running server:
permabrain access-log --tail 20
permabrain access-log --method GET --status 200 --path /api/v1/articles
permabrain access-log --after 2026-06-18T00:00:00Z --limit 100 --jsonOr stream live entries from a running server:
permabrain access-log --follow --url http://localhost:8765 --count 10When the server has a home directory, successful requests are also appended to logs/access-log.jsonl as JSON lines. Rotated files are named access-log.1.jsonl, access-log.2.jsonl, etc. Control persistence with:
# Custom log directory and retention
PERMABRAIN_ACCESS_LOG_DIR=/var/log/permabrain \
PERMABRAIN_ACCESS_LOG_MAX_SIZE=10485760 \
PERMABRAIN_ACCESS_LOG_MAX_FILES=5 \
PERMABRAIN_ACCESS_LOG_RETENTION_DAYS=30 \
permabrain serveThe web viewer has an Audit (👁) tab that renders the persisted access log with filters and an optional live tail.
Every response includes an X-Request-ID header. Pass X-Request-ID in a request to correlate server logs with client traces.
Metrics and monitoring
permabrain serve exposes runtime and aggregate metrics at /api/v1/metrics:
# JSON report (runtime counters + article/attestation totals)
curl http://localhost:8765/api/v1/metrics
# Prometheus-compatible exposition text
curl http://localhost:8765/api/v1/metrics?format=prometheusThe JSON report includes server uptime, request counts, HTTP status buckets, event counters, active stream connections, and the same data totals returned by permabrain metrics. Prometheus output exposes permabrain_runtime_*, permabrain_articles_total, permabrain_attestations_total, and related gauges.
# Start the server with an API key
PERMABRAIN_API_KEY=pb_xxx permabrain serve
# Or pass it on the command line
permabrain serve --api-key pb_xxx
# Use it with the client CLI
permabrain client health --api-key pb_xxx
permabrain client routes --api-key pb_xxxFrom code:
const client = createClient({ baseUrl: 'http://localhost:8765', apiKey: 'pb_xxx' });Key endpoints:
GET /health— server health, transport, agent id, live-stream advertisementGET /api/v1/statusGET /api/v1/articles— query articlesGET /api/v1/articles/:key— get articlePOST /api/v1/articles— publish articlePOST /api/v1/articles/:key/attest— attestGET /api/v1/articles/:key/consensusGET /api/v1/articles/:key/historyGET /api/v1/search?q=...GET /api/v1/dashboardand/api/v1/dashboard.htmlGET /api/v1/log/POST /api/v1/logGET /api/v1/log/export/POST /api/v1/log/importGET /api/v1/log/requests— recent HTTP request ring buffer (memory); add?source=diskto query persisted logsGET /api/v1/log/requests/stream— live Server-Sent Events tail of the access logGET /api/v1/bundles— export a single article bundlePOST /api/v1/bundles— import an article bundleGET /api/v1/export-all— export all indexed articles as a bundleGET /api/v1/history-export?key=...— export a full version history bundlePOST /api/v1/history-import— import a history bundlePOST /api/v1/completion— generate a shell completion scriptGET /api/v1/events/stream— Server-Sent Events real-time streamGET /api/v1/events/ws— WebSocket real-time event streamGET /api/v1/articles/stream— live filtered article/attestation SSE stream
Route discovery and OpenAPI
The server advertises its full route catalog and an OpenAPI 3.0 JSON document:
GET /api/v1/routes— list every registered route with method, auth requirements, parameter shapes, and descriptionsGET /api/v1/openapi.json— OpenAPI 3.0.3 document with paths, security schemes, and operation summaries
Use the client CLI to inspect them:
permabrain client routes
permabrain client openapi
permabrain client routes --url http://peer.example.com:8765 --jsonOr from code:
const client = createClient({ baseUrl: 'http://localhost:8765', apiKey: 'pb_xxx' });
const { routes } = await client.routes();
const spec = await client.openapi();
// Export and import bundles
const bundle = await client.exportBundle({ key: 'person/ada-lovelace' });
const all = await client.exportAll();
const history = await client.exportHistory('person/ada-lovelace');
const recentRequests = await client.requests({ limit: 20, status: 500 });
const mdTable = await client.requestsMarkdown({ limit: 50 });
const importResult = await client.importBundle(bundle);
const historyResult = await client.importHistory(history);
// Generate a shell completion script
const { script } = await client.completion({ shell: 'bash' });When --api-key is set on the server, discovery endpoints require the same key. Pass it with --api-key or PERMABRAIN_API_KEY.
Bundle import accepts { bundle, verify, skipDuplicates } and history import accepts the same shape. Completion accepts { shell: 'bash' | 'zsh' | 'fish' } and returns { script }.
Run permabrain serve --help for details.
Web viewer
permabrain serve also serves a browser UI at / (open http://localhost:8765/ after starting the server). The viewer is a single-file HTML app that works against the local HTTP API and requires no build step. It is also a Progressive Web App: open it on a mobile browser, use Add to Home Screen, and it installs viewer/manifest.json and viewer/service-worker.mjs for offline caching.
Navigation and layout
- Sidebar — search box, topic/author/date filters, sort controls, and toolbar buttons.
- Mobile toggle — on small screens the sidebar collapses behind a hamburger menu.
- Theme toggle — switch between light and dark mode (also configurable in Settings).
- Copy link button — copies a permalink to the current view, filters, selected article, and active tab to the clipboard.
Views and tabs
| Button | View | What it does |
|--------|------|--------------|
| 🏠 Home | Article directory | Browse, search, filter, and sort the local index. Click any article card to open a detail modal with content, metadata, attestations, and consensus. |
| 📊 Dashboard | Local dashboard | Fetches /api/v1/dashboard and shows stats, recent articles, activity feed, and audit-log tail. |
| 📈 Stats | Aggregate stats | Renders /api/v1/metrics data totals: article counts, attestations, top topics, and consensus distribution. |
| 👁 Audit | Request audit log | Shows the persisted HTTP access log (/api/v1/log/requests), with method/status/path filters and a live SSE tail. |
| 🔄 Compare | Diff / merge | Select a base and head article, view a unified diff with conflict markers, and preview or apply a merge. |
| 🔄 Import / Export | Data portability | Import bundles or history from files; export a single article, all articles, history, or the raw DataItem. |
| ✏️ Compose | Article editor | Write a new article in markdown with metadata fields, live preview, localStorage draft recovery, and one-click publish via POST /api/v1/articles. |
| 📤➕ Publish | Batch publish | Drag/drop or pick multiple markdown files, set batch metadata, preview the dry-run report, and publish the whole directory. |
| 🔧 Settings | Preferences | Transport (SSE / WebSocket), theme, default sort, results per page, and live-tail toggle. Persisted to localStorage and reflected in the URL. |
| 📈 Admin | Monitoring panel | Read-only server overview: status, runtime metrics, recent access-log entries, and audit-log tail. |
Search, filters, and sorting
The Home view supports:
- Search — full-text search across article titles and content via
/api/v1/search. - Topic dropdown — auto-populated from the local index.
- Date range — filter articles published after/before a date.
- Author — filter by agent id.
- Sort — by date, title, consensus score, or canonical key.
All choices are encoded in the URL, so copying the link restores the exact filtered view on another device or after a refresh.
Article detail and version history
Clicking an article card opens a modal with:
- Rendered markdown content
- Metadata (author, topic, kind, source URL/name, visibility, ANS-104 id)
- Attestation list with labels (valid, outdated, disputed, wrong) and confidence
- Consensus score
- History tab — every prior version fetched from
/api/v1/articles/:key/history - Raw tab — ANS-104 DataItem fallback and Viewblock/source links
- Decrypt panel — for encrypted articles, paste an X25519 seed to render plaintext locally (seed is never persisted or sent to the server)
Live streams
The viewer subscribes to live updates so the article list and counts refresh as articles or attestations are published:
- SSE —
/api/v1/articles/stream(default, server-pushed events) - WebSocket — same endpoint, upgraded to a WebSocket connection
- Transport indicator — shows which transport is active; click it to cycle preferences
- Failover — if one transport fails, the viewer reconnects and tries the other automatically
The server advertises its preferred transport in /health; the viewer defaults to the server preference unless the URL or Settings override it.
Compose
The Compose view is a complete article editor:
- Markdown textarea with live preview
- Metadata inputs: topic, kind, language, source URL, source name, visibility
- Derived canonical-key hint that updates as you type
Authorization: Bearer <apiKey>header field for protected servers- Publish — POSTs to
/api/v1/articles - Drafts are saved to
localStorageunderpermabrain-compose-draftand restored automatically - Deep-linkable via
?view=compose
Batch publish
The Publish view lets you publish many files at once:
- Drag-and-drop dropzone or multi-file picker
- Batch metadata: topic, kind, source URL/name, language, visibility, recursive option
- Preview — calls
/api/v1/publish-dir/previewand renders a dry-run report - Publish — calls
/api/v1/publish-dirwith files sent as inline{ path, content }arrays - Inline report with succeeded/failed counts and per-file details
- Deep-linkable via
?view=publish
Import and Export
The Import / Export view consolidates data portability:
- Export — download an article bundle, all articles, history, or a raw DataItem as a browser Blob
- Import — pick a bundle or history file, preview the metadata summary, choose dry-run/preview, and POST to
/api/v1/bundlesor/api/v1/history-import - Supports API-key auth for protected servers
- Active tab, export key, and export mode are restored from URL state
Compare
The Compare view visualizes changes between two versions:
- Select a base and head article from dropdowns
- Rendered unified diff with hunks and conflict markers
- Merge preview and apply merge via
/api/v1/merge - Deep-linkable via
?view=compare&compareBase=...&compareHead=...
Settings
Open Settings to change:
- Live transport — SSE or WebSocket
- Theme — light, dark, or auto (follows system preference)
- Default sort — date, title, consensus, key
- Results per page — 5 to 1000
- Live tail — enable/disable automatic live-stream updates
Settings are saved to localStorage and reflected in URL query parameters. The viewer restores them on reload, and the URL takes precedence when sharing a link.
Encrypted articles in the browser
Encrypted articles are rendered with a lock icon and a decrypt panel. Paste the X25519 seed once per session; the seed is kept in memory only. Decryption runs locally in viewer/crypto.mjs using libsodium-style X25519 + XSalsa20-Poly1305 via the browser Web Crypto APIs. The plaintext is never stored or transmitted.
PWA / offline support
viewer/manifest.jsondefines the app name, theme color, icons, and standalone display modeviewer/service-worker.mjscaches the app shell and key assets on install- Offline loads serve the cached HTML; API calls naturally require the server to be reachable again
- The viewer scope is the
viewer/directory, so deep links such as?view=compare&compareBase=a&compareHead=bsurvive install
Multi-Agent Workflow Helpers
permabrain provision-agent reviewer --key-type ed25519
permabrain attest-for-agent <key> --identity-file reviewer.json --valid --confidence 0.9 --reason "Reviewed"
permabrain list-agents
permabrain goal plan.md --execute
permabrain plan plan.md --topic ai --kind subjectCommon Workflows
Publish and attest a source-backed article
permabrain init
permabrain import-wikipedia "Ada Lovelace" --kind person --topic computing
permabrain get person/ada-lovelace
permabrain attest person/ada-lovelace --valid --confidence 0.95 --reason "Well-sourced Wikipedia import"
permabrain consensus person/ada-lovelaceFork, edit, and merge
permabrain fork subject/artificial-intelligence --slug "shorter" --title "AI (short version)"
permabrain list-forks subject/artificial-intelligence
permabrain diff subject/artificial-intelligence person/ai-shorter
permabrain merge subject/artificial-intelligence person/ai-shorter
permabrain sync --dry-runSearch and export a topic
permabrain search "Ada Lovelace" --topic computing --limit 10
permabrain topic computing --sort consensus --limit 20
permabrain export-articles --topic computing --format markdown --output computing.mdEncrypted internal note
permabrain publish-encrypted notes.md --kind subject --topic internal --for "<recipient-public-key>"
permabrain get-encrypted subject/notes --seed-file my-seed.txtBuild and publish a dashboard
permabrain dashboard --output dashboard.html --publish --topic computingPublish a directory of articles
# Publish all .md files in a directory (non-recursive)
permabrain publish-dir ./docs --kind subject --topic ai --source-url https://example.com/docs
# Recursively publish every markdown file under ./docs
permabrain publish-dir ./docs --recursive
# Preview what would be published without writing anything
permabrain publish-dir ./docs --dry-run --recursive --markdown
# Override frontmatter defaults for the whole batch
permabrain publish-dir ./wiki --recursive --kind subject --topic permabrain \
--source-name "Community Wiki" --source-license "CC-BY-SA-4.0"Each file's canonical key is derived from its path: subdirectories become the
topic unless overridden with --topic, and the filename becomes the slug.
Frontmatter inside each markdown file can override the key, kind, topic, or
title for individual files. Re-publishing the same directory updates version
history for articles whose content changed.
Audit everything that happened today
permabrain log --after 2026-06-17T00:00:00Z --order desc --limit 100Canonical Keys
Canonical keys look like this:
person/ada-lovelace
organization/arweave
subject/artificial-intelligence
event/2026-example-event
news/2026/example-storyLocal State
By default PermaBrain writes local state to .permabrain/. Tests and agents can override this with PERMABRAIN_HOME.
.permabrain/
├── config.json
├── keys.json
├── identity-init.json
├── cache/
│ ├── index.json
│ ├── objects/
│ └── pages/
├── logs/
│ └── audit-log.jsonl
└── backups/Private keys stay local. Do not print them. Do not commit them. On first run, permabrain init also writes identity-init.json, which records only public identity metadata: agent id, key type, and creation time.
Supported ANS-104 signing key types:
permabrain init --key-type arweave-rsa4096 # default
permabrain init --key-type ed25519
PERMABRAIN_KEY_TYPE=ed25519 permabrain initANS-104 Uploads
Bundler-compatible endpoints, including HyperBEAM upload surfaces and https://up.arweave.net, expect serialized ANS-104 DataItem bytes.
PermaBrain creates and signs real ANS-104 DataItems with Article-* and Attestation-* tags. Local JSON files are just cache/test artifacts.
Safety
Publishing is public and meant to be permanent. Do not publish secrets, private data, sensitive personal data, or anything you would regret seeing mirrored forever.
Use public sources. Include source URLs. Attribution matters.
HyperBEAM Quickstart
For a copy-paste local HyperBEAM walkthrough, see docs/hyperbeam-quickstart.md.
Watching for updates
Use permabrain watch to poll the configured transport and report new articles or attestations as they appear. The first run records everything currently visible as "seen" so restarts don't flood you with the full history.
# Continuous watch for AI articles (polls every 30 seconds)
permabrain watch --topic ai
# One-off scan, JSON output
permabrain watch --topic ai --once --json
# Watch attestations for a specific article
permabrain watch --key subject/artificial-intelligenceState is persisted to .permabrain/cache/watch-state.json.
Tests
npm test
npm run test:transport-resilience
npm run test:hyperbeam
npm run test:wikipedia
npm run test:arweave
npm run test:public-upload
npm run test:watchTransport calls (HyperBEAM and Arweave) are wrapped with exponential-backoff retries and per-target circuit breakers. After repeated failures the breaker opens for 30 seconds, rejects fast, then tries a few half-open probes before closing. You can inspect breaker state through the probe output or programmatically via api.getCircuitBreakerStatus().
HyperBEAM, Wikipedia, and Arweave integration tests skip cleanly when dependencies are unavailable unless required environment flags are set.
test:arweave is read-only. test:public-upload never publishes unless explicitly enabled:
PERMABRAIN_ENABLE_PUBLIC_UPLOAD=1 npm run test:public-uploadOnly enable public upload when you are ready to publish permanent public test data.
Agent API
Most CLI commands are also available programmatically through src/agent-api.mjs:
import { api } from 'permabrain';
await api.init();
const result = await api.publish({ content: '# AI\n...', kind: 'subject', topic: 'ai', sourceUrl: 'https://example.com/ai' });
const article = await api.get('subject/artificial-intelligence');
const report = await api.doctor({ fix: true });
// Publish every .md file in a directory
const batch = await api.publishDirectory('./docs', {
recursive: true,
kind: 'subject',
topic: 'ai',
sourceName: 'Docs',
sourceLicense: 'CC-BY-SA-4.0'
});
console.log(batch.succeeded, batch.failed);
console.log(await api.publishDirectoryToMarkdown(batch));You can also talk to a running permabrain serve instance with the HTTP client SDK:
import { createClient } from 'permabrain';
const client = createClient({ baseUrl: 'http://localhost:8765' });
await client.health();
const { summary } = await client.publish({
content: '# Ada Lovelace\n...',
kind: 'person',
topic: 'computing',
sourceUrl: 'https://en.wikipedia.org/wiki/Ada_Lovelace'
});
const article = await client.get('person/ada-lovelace');
const { score } = await client.consensus('person/ada-lovelace');
const html = await client.dashboardHTML();
const metrics = await client.metrics(); // JSON runtime + data metrics
const prom = await client.metrics({ format: 'prometheus' }); // Prometheus text
// Batch publish from an inline file batch or a server-local directory
const batch = await client.publishDirectory({
dir: 'docs',
recursive: true,
kind: 'subject',
topic: 'ai',
sourceName: 'Docs',
sourceLicense: 'CC-BY-SA-4.0'
});
console.log(batch.succeeded, batch.count);
// Dry-run preview without publishing
const preview = await client.previewDirectory({
files: [
{ path: 'docs/overview.md', content: '# Overview\n...' }
],
kind: 'subject',
topic: 'ai'
});
console.log(preview.results.map((r) => r.key));
// Markdown report via Accept header
const reportMd = await client.publishDirectoryMarkdown({
files: [
{ path: 'docs/overview.md', content: '# Overview\n...' }
]
});The client mirrors the agent API surface and returns JSON responses from the REST endpoints. Every method rejects with { status, error } when the server responds with a non-2xx status code.
See src/agent-api.mjs and src/client.mjs for the full method lists.
Publishing the Package
To publish a new version to npm:
- Run the full test suite:
npm test - Bump the version:
npm version patch|minor|major - Review the package contents:
npm pack --dry-run - Publish a dry-run to check everything:
npm run publish:dry-run - Publish:
npm publish --access public
package.json files includes src/, scripts/, viewer/, skills/, docs/, README.md, CHANGELOG.md, NEXT-STEPS.md, and package.json. The permabrain bin entry points at scripts/cli.mjs.
