hush

module
v0.5.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT

README ΒΆ

🀫  hush

Discord-gated secrets broker for AI agents

One passphrase. No key files. No dotfiles on agent disks.


Release Go Version License


CI / CD Β Β  Build Last Commit Β Β Β Β  Quality Β Β  Go Report Coverage
Security Β Β  Scorecard Security Β Β Β Β  Community Β Β  Contributors Bitcoin


Project Navigation
πŸš€Β Installation ⚑ QuickΒ Start πŸ“šΒ Documentation
πŸ”Β Security πŸ› οΈΒ CodeΒ Standards πŸ§ͺΒ ExamplesΒ &Β Tests
πŸ€–Β AIΒ Usage βš–οΈΒ License 🀝 Contributing
πŸ‘₯Β Maintainers

hush is a single Go binary that keeps every API key, OAuth token, and service credential encrypted on a single trusted host. Agents request short-lived, scoped sessions over Tailscale; the request is approved on your phone via Discord; approved secrets are delivered ECIES-encrypted end-to-end and injected into the agent process's environment β€” never written to disk on the agent machine.

If your dev workflow runs untrusted code (npm/pip packages, LLM-generated scripts, AI-agent tools that execute shell commands) and your secrets currently live in shell rc files or cloud-provider credential files, hush is for you. Vault, 1Password CLI, and dotfile-based env vars all leave files on disk that commodity malware grep for first. hush makes those files not exist.


                            TAILSCALE MESH
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚                                                                     β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
  β”‚  β”‚  AGENT MACHINE           β”‚     β”‚  VAULT HOST                   β”‚ β”‚
  β”‚  β”‚  (untrusted, clean disk) β”‚     β”‚  (mlocked memory; offline)    β”‚ β”‚
  β”‚  β”‚                          β”‚     β”‚                               β”‚ β”‚
  β”‚  β”‚  interactive client /    β”‚     β”‚  vault server                 β”‚ β”‚
  β”‚  β”‚  supervisor              β”‚     β”‚                               β”‚ β”‚
  β”‚  β”‚       β”‚                  β”‚     β”‚       β–²                       β”‚ β”‚
  β”‚  β”‚       β”‚ ECDSA-signed     │─────┼──────►│ verify signature      β”‚ β”‚
  β”‚  β”‚       β”‚ claim            β”‚     β”‚       β”‚ check Tailscale IP    β”‚ β”‚
  β”‚  β”‚       β”‚                  β”‚     β”‚       β–Ό                       β”‚ β”‚
  β”‚  β”‚       β”‚                  β”‚     β”‚  Discord DM ─────► phone      β”‚ β”‚
  β”‚  β”‚       β”‚                  β”‚     β”‚       β”‚  [Approve]            β”‚ β”‚
  β”‚  β”‚       β”‚ ES256K JWT       │◄────┼──────── issue scoped JWT      β”‚ β”‚
  β”‚  β”‚       β”‚                  β”‚     β”‚       β–Ό                       β”‚ β”‚
  β”‚  β”‚       β”‚ secret fetch     │─────┼──────►│ ECIES-encrypt to      β”‚ β”‚
  β”‚  β”‚       β”‚ (ECIES bytes)    β”‚     β”‚       β”‚ ephemeral pubkey      β”‚ β”‚
  β”‚  β”‚       β–Ό                  β”‚     β”‚       β”‚                       β”‚ β”‚
  β”‚  β”‚  decrypt β†’ env var       β”‚     β”‚       └─────────────────────  β”‚ β”‚
  β”‚  β”‚  inject into child       β”‚     β”‚  [no key files anywhere]      β”‚ β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
  β”‚                                                                     β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ Installation

hush requires a supported release of Go (Go 1.26+) and is built CGO_ENABLED=0 β€” a single static binary.

Status: this is still ALPHA and PR's are welcome to improve the project.

Prerequisites: a vault host and an agent host on the same Tailscale tailnet, plus a Discord bot you control (https://discord.com/developers/applications) for the approval channel.

Build from source
git clone https://github.com/mrz1836/hush.git && cd hush
magex build && sudo install -m 0755 cmd/hush/hush /usr/local/bin/hush

Upgrade

Once hush is on your PATH, the binary can upgrade itself in place from the GitHub releases:

hush upgrade          # download the latest release tarball, verify SHA-256, install in place
hush upgrade --check  # report the latest available version without installing
hush upgrade --force  # reinstall the latest release even when already current

Channel selection is controlled by the UPDATE_CHANNEL environment variable (case-insensitive; default stable):

UPDATE_CHANNEL=stable hush upgrade   # latest non-prerelease (default)
UPDATE_CHANNEL=beta   hush upgrade   # latest prerelease, falls back to stable when none
UPDATE_CHANNEL=edge   hush upgrade   # most recent release of any kind (excludes drafts)

# Or override the env per-invocation with --channel:
hush upgrade --channel beta

The install target is the resolved path of the running binary (os.Executable() with symlinks evaluated β€” typically $(which hush)). hush upgrade requires write access to that directory; when the directory is not writable the command exits with a clear error naming the directory rather than silently installing elsewhere. Re-run the command under sudo (or copy the new binary into place manually) when that happens.

After a successful upgrade hush upgrade prints a Restart any running 'hush serve' to pick up the new version reminder β€” the upgrade does not touch any supervised process.


πŸ—ΊοΈ Command palette

Every hush subcommand at a glance β€” every entry below is real today.

Command Purpose
hush smoke Guided end-to-end test with a fake secret β€” start here
hush init server / hush init client Bootstrap a vault host / enroll an agent host
hush serve Run the vault server (Tailscale-only)
hush secret add / list / remove / rotate Manage vault entries (rotate re-encrypts and hot-reloads)
hush vault rekey Change the vault passphrase β€” rotates the root of trust (TTY-only)
hush request --exec … One-shot interactive fetch + child exec
hush supervise <config.toml> Long-running daemon with grace cache + validators
hush health / server-url / version Daily-driver helpers
hush revoke --jti … Kill an active session token
hush upgrade Self-upgrade from a GitHub release (stable / beta / edge)

Global flags β€” --config <path>, --verbose, --quiet, --no-color β€” work on every command.


⚑ Quick Start

Four flows, in the order you'll use them.


Try it in 60 seconds

One command, fake secret, real Discord approval:

hush smoke --state-dir ~/.hush-smoke --reset

hush smoke walks the setup prompts, creates an isolated test vault, adds HUSH_SMOKE_TEST=hello-from-hush, starts a temporary Tailscale-only server, enrolls a client, asks you to approve in Discord, verifies the fake secret, and shuts the temporary server down.

Smoke uses isolated macOS Keychain entries, hush-smoke-discord / hush-smoke-server, under its smoke state. It does not read, overwrite, or delete the production hush-discord / hush-server bot-token item. A transient clock probe outage is downgraded to a warning in smoke; a measured clock drift outside the configured limit still fails.

Clock checks use read-only SNTP probes against time.apple.com, time.cloudflare.com, and pool.ntp.org, with a 2-second per-provider timeout and first valid offset winning. Successful live probes write a 0600 clock-sync.json recent-good cache under the state dir; if every provider is unavailable later, a cache entry younger than 1 hour can be used as fallback and is audited as clock_sync_cache_fallback. hush init server and hush serve still fail closed on provider outage or measured drift unless the operator explicitly passes --allow-clock-skew, which emits clock_skew_override.

To prove client authentication and approval routing against an already-running server without creating smoke state or touching Keychain/vault state, use an enrolled client key:

hush smoke --against-running --client-key-file ~/.hush-client.key

🧹 Cleanup: hush smoke clean archives smoke artifacts by default. Add --destroy --confirm 'destroy smoke' to permanently delete them.


Bootstrap the vault host

Three commands, run on the host you trust with your secrets.

hush init server                       # interactive preflight + prompts
hush secret add ANTHROPIC_API_KEY      # then OPENAI_API_KEY, GEMINI_API_KEY, …
hush serve                             # binds Tailscale, brokers approvals

πŸ›°οΈ Listen on the vault host's Tailscale IPv4 β€” not your laptop IP. When hush init server asks for a listen address, run printf '%s:7743\n' "$(tailscale ip -4)" on the server host and paste the result. Set discord_approval_channel_id to route approvals to a channel; leave it empty to DM the owner directly.

πŸ” macOS Keychain locked? Choose the env-token fallback during init and run hush serve with HUSH_DISCORD_BOT_TOKEN exported in that terminal. Full recovery flow in docs/SECURITY.md Β§2.4.


Enroll the agent host

Enroll a per-machine client key, then fetch secrets straight into a child process. Nothing lands on disk.

hush init client --machine-index 1
HUSH_SERVER="$(hush --config ~/.hush/config.toml server-url)"

hush request \
  --server "$HUSH_SERVER" \
  --machine-index 1 \
  --scope ANTHROPIC_API_KEY --scope OPENAI_API_KEY --scope GEMINI_API_KEY \
  --max-uses 3 --ttl 10m \
  --reason "claude-code session" \
  --exec zsh

Approve on Discord; the shell you launched inherits all three keys in its environment β€” and only there. They are zeroed the moment the shell exits.

βš™οΈ --scope is repeatable and comma-separated. Either --scope A --scope B or --scope A,B works. --max-uses must be β‰₯ the number of scopes (one fetch per scope per session). --exec names a program, not a shell string β€” pass child arguments after --.


Run a long-running daemon

For agents that run overnight, swap hush request for hush supervise. One approval per refresh window keeps a 24/7 child alive across crashes; the grace cache silently restarts a child that dies inside cache_grace_ttl, so a 3am OOM doesn't page you.

Save the snippet below to ~/.hush/supervisors/hermes.toml β€” the full schema lives in deploy/examples/supervisors/example-daemon.toml:

name                      = "hermes"
reason                    = "Hermes AI gateway"
server_url                = "http://100.64.0.1:7743/h/example"
client_machine_index      = 1
session_type              = "supervisor"
requested_ttl             = "20h"
refresh_window            = "09:00-10:00"
cache_secrets_for_restart = true
cache_grace_ttl           = "60m"
status_socket             = "/tmp/hush/supervise-hermes.sock"
pid_file                  = "/tmp/hush/supervise-hermes.pid"

scope = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY"]

[child]
command = ["/usr/local/bin/hermes", "gateway", "start"]

[validators]
ANTHROPIC_API_KEY = "anthropic"
OPENAI_API_KEY    = "openai"
GEMINI_API_KEY    = "google-ai"

Then:

hush supervise ~/.hush/supervisors/hermes.toml --dry-run   # validate the config
hush supervise ~/.hush/supervisors/hermes.toml             # run for real

Built-in validators (anthropic, anthropic-oauth, openai, google-ai, github) hit each provider before the child starts β€” stale credentials fail loudly instead of breaking your daemon at 3am. Full guide in docs/DAEMONS.md.


πŸ“– More? docs/OPERATIONS.md covers Keychain ACL recovery, clock-skew overrides, and --non-interactive mode for Terraform/Ansible provisioning. docs/CONFIG-SCHEMA.md has the full server + supervisor TOML schemas, and deploy/examples/supervisors/ holds production templates ready to copy.


At a glance

What hush does:

  • Keeps every secret encrypted in a single AES-256-GCM + Argon2id (256MB) vault file on one trusted host.
  • Requires phone approval (Discord DM with interactive buttons) for every fresh session.
  • Delivers secrets ECIES-encrypted end-to-end into agent process memory only β€” no disk writes on the agent.

What hush explicitly does NOT do:

  • No multi-owner approvals β€” a single configured operator approves every request (multi-owner quorum is future scope).
  • No cloud KMS / SaaS dependency. The vault is self-hosted and offline-capable.
  • No public network exposure. The vault server is bound to a Tailscale interface and refuses to start otherwise.

Daily-driver helpers:

  • hush health --server "$(hush server-url)" β€” one-shot reachability + clock-skew check.
  • hush secret list β€” enumerate vault entries (TTY: NAME β€” description; pipe-friendly).
  • hush secret rotate β€” re-encrypt the vault and hot-reload hush serve (SIGHUP, no downtime).
  • hush vault rekey β€” change the vault passphrase itself; snapshots the old vault and prints a restart-required reminder. See docs/VAULT-REKEY.md.
  • hush server-url β€” print the canonical server URL from your TOML config, perfect for $(…) substitution.
  • hush revoke --jti <uuid> β€” kill an active JWT before its TTL expires.

πŸ”€ Operating modes

Two ways to consume secrets; pick by how often you can answer your phone.

Mode Best for Approval cadence
hush request --exec Interactive shells, one-shot scripts, CI jobs Once per invocation
hush supervise 24/7 daemons, AI gateways, long-running agents Once per refresh window (e.g. 09:00–10:00 daily)

Pick request for ad-hoc work; pick supervise when a phone buzz per restart would page you at 3am.


Why hush exists

When untrusted code lands on a developer machine β€” via npm/pip supply-chain attacks, LLM-generated scripts, or trojans masquerading as tools β€” the very first thing it does is enumerate known credential patterns in known files: shell rc files, cloud-provider credential files, .env files, signing keys, and PEM files.

hush exists to make this enumeration return nothing. Secrets stay encrypted on a single trusted host. Agents fetch them only after a human approves the request from a phone. Approved secrets are delivered into process memory and zeroed when the process exits. Nothing on disk.

For the full threat model, see docs/SECURITY.md.


Architecture summary

hush is a single Go binary playing three roles:

  • Vault server β€” holds the encrypted vault file in mlocked memory; issues ES256K-signed JWTs after Discord approval; ECIES-encrypts secret responses to the client's per-session ephemeral public key; exposes a tiny HTTP API over Tailscale only.
  • Interactive client β€” ECDSA-signs a claim with a per-machine BIP32-derived key; receives a JWT after approval; fetches and decrypts secrets; injects them into a child process's environment.
  • Supervisor β€” long-lived per-daemon process that holds the JWT and ephemeral ECIES key in mlocked memory across child crashes/restarts within the session TTL; runs validators before child start; exposes a Unix status socket for agent-visible freshness queries.

Seven security layers stack independently β€” compromise of any single layer does not enable secret extraction:

  1. BIP32 HD key hierarchy β€” all keys derived at runtime from a single passphrase. Zero key files on disk.
  2. ES256K asymmetric JWT signing β€” only the server can sign; leaking the public key does not enable forgery.
  3. ECIES end-to-end secret transport β€” secrets are encrypted to a per-session ephemeral pubkey; captured HTTP traffic shows binary blobs.
  4. ECDSA-signed client requests β€” every claim and revocation is signed with a registered per-machine client key.
  5. mlocked secure memory β€” SecureBytes containers; secrets never stored as Go string; explicit zeroing on shutdown.
  6. Signed hash-chained audit log β€” every event ECDSA-signed; chain breaks on modification.
  7. Obscurity β€” random API path prefix, custom vault file format, non-obvious binary name. Additive only β€” never load-bearing.

The network perimeter is Tailscale-only β€” the vault server refuses to bind on a non-Tailscale interface. Tailscale is the current mesh-VPN choice, but the architecture does not depend on it specifically; the requirement is "no public reachability," and Tailscale satisfies it cleanly. The Approver interface is also pluggable: Discord is the reference implementation and the only one that ships today, but future Slack / Telegram / PagerDuty backends can be wired without changing the rest of the system.

For the full architecture treatment, see docs/ARCHITECTURE.md.


Programmatic integration

AI agents that consume hush β€” Claude Code, Codex, custom Go daemons β€” can integrate in-process via the github.com/mrz1836/hush/pkg/client SDK instead of exec'ing the CLI. The SDK gives agents typed access to two surfaces:

1. Supervisor freshness β€” SupervisorStatus (read what the local supervisor knows; no Discord roundtrip):

import "github.com/mrz1836/hush/pkg/client"

sup := client.NewSupervisorStatus(os.Getenv("HUSH_STATUS_SOCKET"))
status, err := sup.Snapshot(ctx)
if len(status.ScopeStale) > 0 {
    log.Fatal("refusing to run β€” stale scopes:", status.ScopeStale)
}
fmt.Println("session expires at:", status.SessionExpiresAt)

2. Capability discovery β€” Me() (ask the vault server what scopes exist and what does my current session look like, signed with the agent's enrolled client key, without burning a Discord approval):

resp, err := client.Me(ctx, client.MeRequest{
    ServerURL:   "http://100.64.0.1:7743/h/abcd1234",
    ClientKey:   myEnrolledPrivKey,           // *ecdsa.PrivateKey
    MachineName: "claude-code-laptop",
    BearerJWT:   os.Getenv("HUSH_BEARER"),    // optional
})
if err != nil {
    log.Fatal(err)
}
fmt.Println("scopes available:", resp.ScopesAvailable)
if resp.CurrentSession != nil {
    fmt.Println("current jti:", resp.CurrentSession.JTI,
        "expires:", resp.CurrentSession.ExpiresAt)
}

Together these let an agent plan: "do I already hold a fresh session for this scope? when does it expire? what could I request if I need more?" β€” all without touching the operator's phone.

See pkg/client/README.md for the full v1 surface. The SDK ships typed errors (ErrSocketUnavailable, ErrInvalidResponse, ErrRefreshDenied, ErrUnauthenticated) so callers can switch on them with errors.Is.

3. Lifecycle events β€” Watch() (reactive notification so an agent can wind down BEFORE its credentials expire, instead of being killed mid-task):

events, _ := sup.Watch(ctx, client.WatchOptions{
    PollInterval:     30 * time.Second,
    ExpiryThresholds: []time.Duration{15 * time.Minute, 5 * time.Minute, 1 * time.Minute},
})
for ev := range events {
    switch ev.Type {
    case client.EventExpiresSoon:
        if ev.Threshold <= time.Minute {
            checkpoint(); shutdownCleanly()
        }
    case client.EventStateChange:
        log.Info("supervisor state β†’", ev.Status.State)
    case client.EventSessionRenewed:
        log.Info("fresh JTI", ev.Status.SessionJTI)
    }
}

Watch() emits EventInitial, EventStateChange, EventScopeHealthChange, EventSessionRenewed, EventExpiresSoon (once per configured threshold per session), and EventError for transient poll failures. The channel closes on context cancel.

Worked example: a runnable program demonstrating all three SDK calls (Snapshot, Me, Watch) plus the agent-context flags on /claim lives at examples/agent/. See docs/AGENT-INTEGRATION.md for the complete agent integration guide.


Tech stack
  • Go 1.26+ β€” single static binary, built CGO_ENABLED=0 exclusively.
  • decred/dcrd/dcrec/secp256k1/v4 β€” secp256k1 primitives used for ECDSA signing, ES256K JWTs, and ECIES envelope encryption.
  • decred/dcrd/hdkeychain/v3 β€” BIP32 HD key derivation from the operator passphrase, paired with stdlib golang.org/x/crypto/argon2 for the KDF.
  • Tailscale β€” the only network reachable to the vault server. WireGuard underneath; identity-based ACLs above.
  • Discord + discordgo β€” phone-based approval channel; the reference Approver implementation.
  • golang-jwt/jwt v5 β€” JWT framework; hush registers a custom ES256K signing method.
  • go-toml v2 β€” strict TOML parsing for server and supervisor configs.
  • zalando/go-keyring β€” OS keychain access with ACL support.
  • cobra + pflag β€” CLI subcommand routing and flag parsing.

The SecureBytes mlock pattern is custom-implemented in internal/vault/securebytes/; the design is inspired by sigil but takes no dependency on it.


πŸ“š Documentation

View the comprehensive documentation for hush:

Doc Purpose
docs/OPERATIONS.md Setup, day-to-day modes, --non-interactive, Keychain recovery
docs/VAULT-REKEY.md Vault passphrase rotation runbook: secret rotate vs vault rekey, Keychain follow-up, snapshot/rollback
docs/ARCHITECTURE.md Component model, trust boundaries, supervisor lifecycle
docs/SECURITY.md Threat model, 7 security layers, residual risks
docs/AGENT-INTEGRATION.md SDK guide for AI agents: pkg/client Snapshot / Me / Watch + agent-context flags on /claim
docs/CONFIG-SCHEMA.md Server + supervisor TOML schemas, defaults, validation
docs/DAEMONS.md Supervisor pattern, refresh tuning, validator authoring
docs/API.md HTTP endpoint reference
docs/LIFECYCLE-SCENARIOS.md Supervisor lifecycle scenarios β€” behavioral reference
docs/TAILSCALE-ACLS.md Recommended ACL pattern restricting the vault port
docs/CLEAN-MACHINE.md Agent-machine cleanup checklist

πŸ” Security

Important Disclaimer

⚠️ Experimental Software β€” Use at Your Own Risk

hush is experimental, open-source software provided "AS-IS" without warranty. By running it, you acknowledge:

  • Status: ALPHA quality; expect bugs, edge cases, and breaking changes.
  • No formal audit: hush has not been professionally audited or penetration-tested.
  • You own the host: the trusted vault host, your Tailscale config, and your Discord bot are yours to secure β€” hush only does its part.
  • No liability: the authors accept no responsibility for compromised secrets, downtime, or damages.

Don't trust hush with a secret you can't afford to rotate. If it breaks, you get to keep both pieces.

For the full threat model and the 7 security layers, see docs/SECURITY.md. For security issues, see our Security Policy or contact: hush@mrz1818.com.


Additional Documentation & Repository Management
Development Setup (Getting Started)

Install MAGE-X build tool for development:

# Install MAGE-X for development and building
go install github.com/magefile/mage@latest
go install github.com/mrz1836/go-mage/magex@latest
magex update:install
Build Commands

View all build commands:

magex help

Common commands:

  • magex build β€” Build the binary
  • magex test β€” Run test suite
  • magex lint β€” Run all linters
  • magex deps:update β€” Update dependencies
Binary Deployment

This project uses goreleaser for streamlined binary deployment to GitHub. To get started, install it via:

brew install goreleaser

The release process is defined in the .goreleaser.yml configuration file. Then create and push a new Git tag using:

magex version:bump bump=patch push=true branch=master

This process ensures consistent, repeatable releases with properly versioned artifacts.

GitHub Workflows

hush uses the Fortress workflow system for comprehensive CI/CD:

  • fortress-test-suite.yml β€” Complete test suite across multiple Go versions
  • fortress-code-quality.yml β€” Code quality checks (gofmt, golangci-lint, staticcheck)
  • fortress-security-scans.yml β€” Security vulnerability scanning
  • fortress-coverage.yml β€” Code coverage reporting to Codecov
  • fortress-release.yml β€” Automated binary releases via GoReleaser

See all workflows in .github/workflows/.

Updating Dependencies

To update all dependencies (Go modules, linters, and related tools), run:

magex deps:update

This command ensures all dependencies are brought up to date in a single step, including Go modules and any managed tools. It is the recommended way to keep your development environment and CI in sync with the latest versions.


πŸ§ͺ Examples & Tests

All unit tests run via GitHub Actions. View the configuration file.

Run all tests (fast):

magex test

Run all tests with race detector (slower):

magex test:race

Test Coverage

View coverage report:

magex test:coverage

Coverage reports are automatically uploaded to Codecov on every commit.


Benchmarks

Baseline performance numbers for the hot crypto paths. Re-measure after any refactor that touches the request path, vault loader, or transport encryption β€” regressions land here before they land in production.

Run the suite:

magex bench:default time=2s

Capture a baseline file for diffs across branches:

magex bench:save time=2s out=bench.txt

Compare two runs:

magex bench:compare old=before.txt new=after.txt

Latest baseline β€” Apple M1 Max Β· darwin/arm64 Β· Go 1.26 Β· benchtime=2s:

Benchmark ns/op B/op allocs/op Path covered
BenchmarkValidate (internal/token) 192,871 5,739 105 JWT parse + ES256K verify + store lookup (supervisor)
BenchmarkEncrypt (internal/transport/ecies) 173,705 3,345 40 Ephemeral keygen + ECDH + AES-CBC + HMAC envelope
BenchmarkDecrypt (internal/transport/ecies) 151,457 2,528 29 Pubkey parse + ECDH + KDF + HMAC verify + AES-CBC
BenchmarkLoad (internal/vault) 51,813 16,528 113 Encrypted-vault read (16 secrets, ~64 B each)

Numbers are local-machine baselines, not SLOs. Use them to spot β‰₯10% regressions on the same hardware after a code change. The CI machine numbers will differ; track relative deltas, not absolutes.

Last measured: 2026-05-24


πŸ› οΈ Code Standards

Read more about this Go project's code standards.


πŸ€– AI Usage & Assistant Guidelines

Read the AI Usage & Assistant Guidelines for details on how AI is used in this project and how to interact with AI assistants.


πŸ‘₯ Maintainers

MrZ
MrZ

🀝 Contributing

View the contributing guidelines and please follow the code of conduct.

How can I help?

All kinds of contributions are welcome πŸ™Œ! The most basic way to show your support is to star 🌟 the project, or to raise issues πŸ’¬. You can also support this project by becoming a sponsor on GitHub πŸ‘ or by making a bitcoin donation to ensure this journey continues indefinitely! πŸš€

Stars


πŸ“ License

License

This project is licensed under the terms of the LICENSE file at the repo root.

Directories ΒΆ

Path Synopsis
cmd
hush command
Package main is the hush binary entry point.
Package main is the hush binary entry point.
examples
agent command
Package main is a worked example of an AI agent integrating with hush via the public pkg/client SDK.
Package main is a worked example of an AI agent integrating with hush via the public pkg/client SDK.
internal
audit
Package audit owns the hush server's tamper-evident audit log (Constitution III Layer 6).
Package audit owns the hush server's tamper-evident audit log (Constitution III Layer 6).
cli
Package cli is the operator-facing command-line surface of the hush binary.
Package cli is the operator-facing command-line surface of the hush binary.
cli/setup
Package setup hosts the diagnostic-first foundations the guided `hush init server` flow depends on: a structured error taxonomy (errors.go), a preflight check registry (preflight.go), and an existing-state classifier (state.go) that labels pre-existing config / vault / state-dir / Keychain artifacts as safe-to-reuse, repairable, or collision.
Package setup hosts the diagnostic-first foundations the guided `hush init server` flow depends on: a structured error taxonomy (errors.go), a preflight check registry (preflight.go), and an existing-state classifier (state.go) that labels pre-existing config / vault / state-dir / Keychain artifacts as safe-to-reuse, repairable, or collision.
config
Package config owns the server-side TOML configuration file: schema, defaults, validation, and path-safety checks.
Package config owns the server-side TOML configuration file: schema, defaults, validation, and path-safety checks.
discord
Package discord owns the Discord-backed approval surface.
Package discord owns the Discord-backed approval surface.
discord/alerts
Package alerts is the operator-facing Discord alert surface for hush.
Package alerts is the operator-facing Discord alert surface for hush.
keychain
Package keychain wraps the platform-native OS keychain operations behind a single Keychain interface.
Package keychain wraps the platform-native OS keychain operations behind a single Keychain interface.
keys
Package keys derives every cryptographic key used by hush at runtime from a single passphrase, with no key material ever written to disk.
Package keys derives every cryptographic key used by hush at runtime from a single passphrase, with no key material ever written to disk.
logging
Package logging provides the project's single slog.Logger constructor and the credential-redaction primitives every log call passes through.
Package logging provides the project's single slog.Logger constructor and the credential-redaction primitives every log call passes through.
redact
Package redact provides best-effort scrubbing of likely-secret patterns from operator-visible strings such as the /claim command_preview field.
Package redact provides best-effort scrubbing of likely-secret patterns from operator-visible strings such as the /claim command_preview field.
server
Package server: POST /claim handler.
Package server: POST /claim handler.
supervise
Supervised child process layer.
Supervised child process layer.
supervise/config
Package config owns the per-supervisor TOML configuration file: schema, defaults, validation, and path-safety checks.
Package config owns the per-supervisor TOML configuration file: schema, defaults, validation, and path-safety checks.
supervise/validators
Package validators provides the pre-flight credential Validator interface plus five built-in implementations (anthropic, anthropic-oauth, openai, google-ai, github).
Package validators provides the pre-flight credential Validator interface plus five built-in implementations (anthropic, anthropic-oauth, openai, google-ai, github).
supervise/watchdog
Package watchdog implements an alert-only log-pattern watchdog for hush supervisor child processes.
Package watchdog implements an alert-only log-pattern watchdog for hush supervisor child processes.
testutil
Package testutil is the project's shared, test-only harness package.
Package testutil is the project's shared, test-only harness package.
token
Package token implements the Layer-2 session-authority for hush: ES256K JWT issuance, validation, in-memory revocation/use-count store, and the cleanup loop that reclaims expired live records.
Package token implements the Layer-2 session-authority for hush: ES256K JWT issuance, validation, in-memory revocation/use-count store, and the cleanup loop that reclaims expired live records.
transport/ecies
Package ecies implements the Layer-3 wire-level secret-response encryption for hush: BIE1 ECIES over secp256k1 (4-byte magic, 33-byte compressed ephemeral public key, AES-256-CBC ciphertext, 32-byte HMAC-SHA256 tag).
Package ecies implements the Layer-3 wire-level secret-response encryption for hush: BIE1 ECIES over secp256k1 (4-byte magic, 33-byte compressed ephemeral public key, AES-256-CBC ciphertext, 32-byte HMAC-SHA256 tag).
transport/sign
Package sign implements the Layer-4 request-signing protocol for hush: canonical-JSON encoding, ECDSA sign/verify over SHA-256(canonical), and nonce + timestamp replay protection.
Package sign implements the Layer-4 request-signing protocol for hush: canonical-JSON encoding, ECDSA sign/verify over SHA-256(canonical), and nonce + timestamp replay protection.
upgrade
Package upgrade implements the hush self-upgrade pattern: resolve a GitHub release for the requested channel, download the platform- specific tarball, verify its SHA256 against the published checksums file, extract it with Zip-Slip / zip-bomb defenses, and atomically replace the running binary via a copy-and-rename dance that is safe against the SIGBUS class of in-place binary mutation bugs.
Package upgrade implements the hush self-upgrade pattern: resolve a GitHub release for the requested channel, download the platform- specific tarball, verify its SHA256 against the published checksums file, extract it with Zip-Slip / zip-bomb defenses, and atomically replace the running binary via a copy-and-rename dance that is safe against the SIGBUS class of in-place binary mutation bugs.
vault
Package vault is the on-disk secret store: an AES-256-GCM-encrypted file containing JSON-encoded name β†’ SecureBytes mappings, written via temp- file + fsync + atomic rename, with strict permission enforcement at both open and save paths.
Package vault is the on-disk secret store: an AES-256-GCM-encrypted file containing JSON-encoded name β†’ SecureBytes mappings, written via temp- file + fsync + atomic rename, with strict permission enforcement at both open and save paths.
vault/securebytes
Package securebytes provides the SecureBytes container β€” an opaque, pointer-only secret holder that pins its payload in non-swappable memory, zeroes the payload on explicit Destroy AND on garbage collection (via a runtime finalizer), and renders as the literal string "[redacted]" through every standard log/format/JSON path.
Package securebytes provides the SecureBytes container β€” an opaque, pointer-only secret holder that pins its payload in non-swappable memory, zeroes the payload on explicit Destroy AND on garbage collection (via a runtime finalizer), and renders as the literal string "[redacted]" through every standard log/format/JSON path.
pkg
client
Package client is the public Go SDK for interacting with hush from an agent process.
Package client is the public Go SDK for interacting with hush from an agent process.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL