hadron is the command-line interface to the Hadron
AI-memory platform, for humans working in a terminal and AI agents
shelling out to it.
Implemented v1 command surface: auth login|logout|whoami|status,
memory list|get|set|rm|clone|export, node list|get|add|update|rm,
spec list|get|describe|register|find|new|edit|extract|link|lint|supersede|use,
app list|install|uninstall|use, config get|set|list, api (raw
GraphQL escape hatch), replace, version, completion, agentic-usage.
spec import spec-kit|code is reserved as a stable command stub but is not yet
implemented.
Specs follow a legal-code citation scheme — flat (<module>:<feature>:<rule>)
or product-rooted (<product>:<module>:<feature>:<rule>) for a multi-product
corpus — with a general-provisions contract at every tier (feature :00,
module :000, product :gen). See
docs/how-to/maintain-product-specs.md.
brew tap hadron-memory/hadron-cli
brew install --cask hadronDownload the archive for your platform from the
latest release,
verify it, and put hadron on your PATH.
checksums.txt is signed with cosign keyless, so
you can confirm it came from this repo's release pipeline before trusting the
checksums in it. Download checksums.txt and checksums.txt.sigstore.json
(the Sigstore bundle — cert + signature in one file) from the release, then:
# 1. Verify the checksum manifest was signed by this repo's release workflow.
cosign verify-blob \
--bundle checksums.txt.sigstore.json \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/hadron-memory/hadron-cli/\.github/workflows/release\.yml@refs/tags/v' \
checksums.txt
# 2. Only once that prints "Verified OK", check your archive against it.
shasum -a 256 -c checksums.txt --ignore-missingThe --certificate-identity-regexp pins the signer to the release workflow on a
v* tag in this repo — without it, cosign would accept a signature from any
GitHub identity. Verifying the signature first is what makes the checksum
meaningful: the manifest is co-hosted with the archives, so checking an archive
against an unsigned checksums.txt only proves they agree with each other.
On Windows, run the above from WSL or Git Bash, or use the native
cosign.exe with
PowerShell's Get-FileHash checksums.txt for the checksum step.
go install github.com/hadron-memory/hadron-cli/cmd/hadron@latest(go install builds without the version stamp — hadron version reports dev.)
make build # produces bin/hadron, version-stampedRequires Go (see go.mod for the version).
hadron auth login # browser OAuth via GitHub (default)
hadron auth login --provider google # browser OAuth via Google
hadron auth whoami
hadron memory list --json
hadron api 'query { me { id email } }'For CI/scripting, mint a token with hadron auth token create, then set
HADRON_TOKEN or pipe it to hadron auth login --with-token. None of the
ways to authenticate require the web portal — a self-hosted hadron-server
is enough; see Authentication.
Run hadron agentic-usage — it prints the full output contract,
stable exit codes, and recipes in one document. Every command
supports --json with stable field names.
This repo is also a Claude Code plugin marketplace. In Claude Code:
/plugin marketplace add hadron-memory/hadron-cli
/plugin install hadron-cli@hadron-cli
The hadron-cli plugin ships a use-hadron-cli skill that teaches
the agent the CLI contract (auth checks, --json, --yes on
destructive commands, fully-qualified node URNs) and defers to
hadron agentic-usage as the runtime source of truth.
hadron is the open-source counterpart to the closed-source portal: the
spec-040 headless-run surface (cor:agt:010) is fully drivable from the CLI, so
a self-hosted deployment needs no portal to schedule and operate agent runs. A
run executes an entry (prompt) node under an App's identity, off any
interactive session; run, schedule, and webhook are three triggers into the
same kernel, and ticket is the action-budget ledger.
End to end — pick the model, name the work, schedule it, watch the runs:
# 1. AI config the runs will use (App→Agent→Org→Host walk; innermost wins).
printf '%s' "$ANTHROPIC_KEY" | hadron ai-config create --app acme.com:ops \
--name default --provider anthropic --model claude-opus-4-8 --api-key -
# 2. The entry node — an ordinary prompt/task node in one of the App's memories.
hadron node add -m acme.com::ops --loc tasks:nightly-digest --type task \
--name 'Nightly digest' --content 'Summarize today's incidents and email the team.'
# 3. Schedule it. --as-self runs on behalf of you, so it can reach your
# personal memories (an App-key caller cannot use --as-self).
hadron schedule create --app acme.com:ops --name nightly-digest \
--cron '0 6 * * *' --tz America/New_York \
--entry acme.com::ops::tasks:nightly-digest --as-self
# 4. Mint the outbound-comms budget the runs consume (org ADMIN).
hadron ticket mint --org acme.com --action comm.outbound --count 100 \
--note 'nightly digest sends'
# 5. Trigger one now to test, waiting for the terminal status; then audit.
hadron run trigger --app acme.com:ops \
--entry acme.com::ops::tasks:nightly-digest --as-self --wait --json
hadron run list --app acme.com:ops --status FAILED --json
hadron run get <run-id> --json # budgets, policy, failure payloadWebhooks are the push variant: hadron webhook create --app <ref> --name <n> --entry <node-urn>
prints a URL path and platform token once (POST to fire the run) — capture
them then, as the secret is never queryable again. hadron webhook rotate <id>
reissues it. Full flag reference: hadron agentic-usage.
Everything the platform can do is meant to be drivable from hadron. The
headless-run surface above closes the spec-040 gap. Some server GraphQL
operations have no dedicated command by design — they are reachable through
the hadron api raw-GraphQL escape hatch when needed:
- Interactive / portal-only flows — chat and session lifecycle
(
startChat,sendChatMessage,startSession, …), the system-memory editor lock and revision/version machinery, multipart asset upload, and invitation / onboarding / profile mutations. These are browser-interaction shaped, not batch-CLI shaped. - Git-sync internals —
replaceSubtree,mergeMemories/mergeNodes,pushMemoryToGit/syncMemory,setMemorySourceToken,encryptMemory. These are server↔git machinery; the CLI'smemory export/node importcover the end-user round-trip. - Not-yet-built, tracked as follow-ups — the Agent surface (
agentCRUD, AI config wiring, subscriptions, imports) has no command group yet; one-time schedules (schedule create --at <iso>), and the admin grants/policy/quota surface are blocked on server work (hadron-server#510, #501). See issue #134's comment thread for the running list.
make build # build with version stamp
make test # go test ./...
make lint # golangci-lint run
make generate # regenerate genqlient code from schema/schema.graphql
make schema # refresh schema snapshot from ../hadron-server, then generate
make schema-check # fail if the committed snapshot is stale vs ../hadron-serverThe GraphQL schema snapshot at schema/schema.graphql is exported
from hadron-server (pnpm schema:export there) and committed here;
typed operations live in internal/api/queries/*.graphql and are
compiled by genqlient. CI fails
if generated code drifts from the committed schema.
That per-PR check only compares the generated client against the
committed snapshot, so it can't catch the snapshot itself lagging the
server (e.g. a server-side appId → appRef rename). make schema-check
closes that gap: it re-exports the SDL from ../hadron-server and fails
if the generated client would change for an operation the CLI uses
(server changes the CLI doesn't touch are ignored). The
schema-drift workflow runs it
nightly against the server's main; enable it by adding a
HADRON_SERVER_RO_TOKEN repo secret (a token with read access to the
private hadron-server repo). Without the secret the job no-ops.
Build and test locally first, then cut the release by pushing a semver tag:
git checkout main && git pull # release from a green main
make build && make test # verify the build works
./bin/hadron --help # sanity check
git tag -a v0.3.0 -m "v0.3.0" # bump per semver
git push origin v0.3.0The tag triggers .github/workflows/release.yml,
which runs goreleaser (.goreleaser.yaml) to:
- cross-compile darwin/linux/windows (amd64/arm64) binaries — version-stamped
from the tag — into archives +
checksums.txt; - sign
checksums.txtwith cosign keyless (GitHub OIDC — no key material), emitting thechecksums.txt.sigstore.jsonbundle (the job needsid-token: write; see the verification steps under Release archives); - codesign + notarize the macOS binaries — only when the signing secrets are configured (see macOS signing below); otherwise skipped and the release ships unsigned;
- publish a GitHub Release with those assets and an auto-generated changelog;
- push the Homebrew cask bump to
homebrew-hadron-cli,
so
brew upgrade --cask hadronpicks it up.
The cask push uses the HOMEBREW_TAP_TOKEN repo secret (a token with write
access to the tap). If a release fails at the cask step, that token has expired
or lost access — rotate it; nothing else needs a secret beyond the workflow's
GITHUB_TOKEN (cosign signing is keyless — no secret).
goreleaser codesigns (Developer ID Application) and notarizes the macOS binaries via quill, which runs on the Linux runner — no macOS runner needed. It's gated on the signing secrets being present, so releases keep working unsigned until they're configured. Until then the Homebrew cask strips the Gatekeeper quarantine flag on install; once notarized releases are verified, that strip is removed (#158).
To enable it, you set up five repository secrets — two from a signing certificate, three from a notary API key. One-time setup (an Apple Developer account is required); do it on a Mac, ~15 minutes.
goreleaser signs with a Developer ID Application certificate. What it needs is
a .p12 file bundling the certificate and its private key, base64-encoded.
-
Create the certificate. In the Apple Developer portal, Certificates → +, choose Developer ID Application. When asked which intermediate, pick G2 Sub-CA (the current one). It has you upload a CSR — generate that with Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority (save to disk). That creates the matching private key in your
loginkeychain — essential; don't delete it. Download the issueddeveloperID_application.cer. -
Import it into the
loginkeychain (not System, not iCloud). Drag the.ceronto the login keychain in Keychain Access's sidebar, or double-click and chooseloginif prompted. The cert must land in the same keychain as its private key, or the.p12export won't be possible."This certificate is not trusted" is expected and harmless — it only means the Apple intermediate isn't installed locally; signing and notarization validate against Apple's servers regardless. To clear it, download Developer ID - G2 from https://www.apple.com/certificateauthority/ and double-click it. Do not set a manual "Always Trust" override — that breaks
codesign. -
Export the
.p12. Select the login keychain → My Certificates. Find Developer ID Application: … — it must have a ▸ disclosure triangle revealing a private key underneath (the "My Certificates" category only lists certs whose key is present). Right-click that row → Export → format Personal Information Exchange (.p12) → save and set an export password.Export greyed out? The cert and key are in different keychains (usually the cert got imported into System). Redo step 2 into
login. -
Turn it into the two secrets:
base64 -i "Developer ID Application.p12" | pbcopy # copies to clipboard
MACOS_SIGN_P12— paste that base64 blobMACOS_SIGN_PASSWORD— the export password from step 3
Notarization authenticates to Apple with an App Store Connect API key — separate from the certificate.
- In App Store Connect → Users and Access → Integrations → App Store Connect API → Team Keys, click +, name it, role Developer, Generate.
- Copy the Issuer ID (a UUID, shown above the table) and the row's Key ID,
and Download the
AuthKey_XXXXXXXXXX.p8(downloadable once — keep it). - Turn them into the three secrets:
base64 -i AuthKey_XXXXXXXXXX.p8 | pbcopy # copies to clipboard
MACOS_NOTARY_ISSUER_ID— the Issuer ID (UUID)MACOS_NOTARY_KEY_ID— the Key IDMACOS_NOTARY_KEY— paste that base64 blob
For each of the five: repo Settings → Secrets and variables → Actions → New repository secret, enter the exact name above, paste the value, save. The next tagged release then signs + notarizes automatically — nothing else to flip (the config is gated on all five being present).
Maintenance: the Developer ID certificate expires (~5 years) — a release
starts failing when it lapses, so renew and re-export the .p12 then. Windows
codesigning (a separate EV-cert lift) is out of scope.
Verify from the Actions run, the new
release, and the
goreleaserbot cask commit in the tap.
- Auth (v1): OAuth authorization-code + PKCE with a loopback
redirect on 127.0.0.1. The server matches redirect URIs exactly, so
each login binds the port first and registers a fresh public client
via dynamic client registration. The resulting token is a
long-lived
hdr_user_*personal access token (no refresh tokens in v1). Stored in the OS keychain, falling back to~/.config/hadron/auth.json(0600). A device-flow strategy can slot in behindinternal/auth.Strategyonce the server supports RFC 8628. - Output: commands marshal explicit DTOs, never generated
GraphQL structs, so
--jsonshapes stay stable across schema regenerations. - Exit codes are documented contract — see
hadron agentic-usage.
The Hadron Claude Code slash commands — /hadron:h-task, /hadron:h-search,
/hadron:h-open-node — live in their own marketplace,
hadron-memory/hadron-plugins,
so they're available without the CLI:
/plugin marketplace add hadron-memory/hadron-plugins
/plugin install hadron@hadron