Skip to content

Repository files navigation

hadron-cli

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.

Status

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.

Install

Homebrew (macOS)

brew tap hadron-memory/hadron-cli
brew install --cask hadron

Release archives (macOS, Linux, Windows)

Download 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-missing

The --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

go install github.com/hadron-memory/hadron-cli/cmd/hadron@latest

(go install builds without the version stamp — hadron version reports dev.)

From source

make build        # produces bin/hadron, version-stamped

Requires Go (see go.mod for the version).

Quick start

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.

For AI agents

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.

Claude Code plugin

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.

Driving headless runs

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 payload

Webhooks 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.

CLI coverage and deliberate exclusions

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 internalsreplaceSubtree, mergeMemories/mergeNodes, pushMemoryToGit/syncMemory, setMemorySourceToken, encryptMemory. These are server↔git machinery; the CLI's memory export/node import cover the end-user round-trip.
  • Not-yet-built, tracked as follow-ups — the Agent surface (agent CRUD, 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.

Development

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-server

The 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 appIdappRef 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.

Releasing

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.0

The 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.txt with cosign keyless (GitHub OIDC — no key material), emitting the checksums.txt.sigstore.json bundle (the job needs id-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 hadron picks 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).

macOS signing (notarization)

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.

Part 1 — the signing certificate → MACOS_SIGN_P12, MACOS_SIGN_PASSWORD

goreleaser signs with a Developer ID Application certificate. What it needs is a .p12 file bundling the certificate and its private key, base64-encoded.

  1. 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 login keychain — essential; don't delete it. Download the issued developerID_application.cer.

  2. Import it into the login keychain (not System, not iCloud). Drag the .cer onto the login keychain in Keychain Access's sidebar, or double-click and choose login if prompted. The cert must land in the same keychain as its private key, or the .p12 export 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.

  3. 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.

  4. Turn it into the two secrets:

    base64 -i "Developer ID Application.p12" | pbcopy   # copies to clipboard
    • MACOS_SIGN_P12 — paste that base64 blob
    • MACOS_SIGN_PASSWORD — the export password from step 3

Part 2 — the notary API key → MACOS_NOTARY_* (three secrets)

Notarization authenticates to Apple with an App Store Connect API key — separate from the certificate.

  1. In App Store ConnectUsers and Access → Integrations → App Store Connect API → Team Keys, click +, name it, role Developer, Generate.
  2. 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).
  3. 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 ID
    • MACOS_NOTARY_KEY — paste that base64 blob

Part 3 — add the secrets to GitHub

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.

Architecture notes

  • 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 behind internal/auth.Strategy once the server supports RFC 8628.
  • Output: commands marshal explicit DTOs, never generated GraphQL structs, so --json shapes stay stable across schema regenerations.
  • Exit codes are documented contract — see hadron agentic-usage.

Hadron slash commands (separate plugin)

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

Releases

Packages

Contributors

Languages