An open API service indexing awesome lists of open source software.

https://github.com/mrz1836/hush

🀫 Discord-gated secrets broker for AI agents β€” encrypted vault, JWT sessions, Tailscale-only
https://github.com/mrz1836/hush

bip32 bitcoin crypto discord go jwt secrets security tailscale

Last synced: about 1 month ago
JSON representation

🀫 Discord-gated secrets broker for AI agents β€” encrypted vault, JWT sessions, Tailscale-only

Awesome Lists containing this project

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](https://golang.org/doc/devel/release.html#policy)
(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
() for the approval channel.

### Build from source

```bash
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](https://github.com/mrz1836/hush/releases):

```bash
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`):

```bash
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 ` | 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 `, `--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:**

```bash
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.

> 🧹 **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.

```bash
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`](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.

```bash
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`](deploy/examples/supervisors/example-daemon.toml):

```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:

```bash
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`](docs/DAEMONS.md).


> πŸ“– **More?** [`docs/OPERATIONS.md`](docs/OPERATIONS.md) covers Keychain
> ACL recovery, clock-skew overrides, and `--non-interactive` mode for
> Terraform/Ansible provisioning. [`docs/CONFIG-SCHEMA.md`](docs/CONFIG-SCHEMA.md)
> has the full server + supervisor TOML schemas, and
> [`deploy/examples/supervisors/`](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 (v0.1.0):**

- No multi-owner approvals (a single configured operator approves; multi-owner is post-v0.1.0 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`](docs/VAULT-REKEY.md).
- `hush server-url` β€” print the canonical server URL from your TOML config, perfect for `$(…)` substitution.
- `hush revoke --jti ` β€” 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`](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** (Constitution Principle VI).
Tailscale is the v0.1.0 mesh-VPN choice; 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 v0.1.0 reference implementation** and the
only one that ships, 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`](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`](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):

```go
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):

```go
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`](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):

```go
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/`](examples/agent/). See
[`docs/AGENT-INTEGRATION.md`](docs/AGENT-INTEGRATION.md) for the
complete agent integration guide.


### Tech stack

- **[Go 1.26+](https://go.dev/)** β€” single static binary, `CGO_ENABLED=0`
exclusively (Constitution Principle IX).
- **[decred/dcrd/dcrec/secp256k1/v4](https://github.com/decred/dcrd)** β€”
secp256k1 primitives used for ECDSA signing, ES256K JWTs, and ECIES
envelope encryption (Constitution Principle III).
- **[decred/dcrd/hdkeychain/v3](https://github.com/decred/dcrd)** β€” BIP32
HD key derivation from the operator passphrase (Constitution Principle
III); paired with stdlib `golang.org/x/crypto/argon2` for the KDF.
- **[Tailscale](https://tailscale.com/)** β€” the only network reachable to
the vault server. WireGuard underneath; identity-based ACLs above.
- **[Discord](https://discord.com/)** + **[discordgo](https://github.com/bwmarrin/discordgo)**
β€” phone-based approval channel; the v0.1.0 reference Approver.
- **[golang-jwt/jwt v5](https://github.com/golang-jwt/jwt)** β€” JWT framework;
hush registers a custom `ES256K` signing method.
- **[go-toml v2](https://github.com/pelletier/go-toml)** β€” strict TOML
parsing for server and supervisor configs.
- **[zalando/go-keyring](https://github.com/zalando/go-keyring)** β€” OS
keychain access with ACL support.
- **[cobra](https://github.com/spf13/cobra)** + **[pflag](https://github.com/spf13/pflag)** β€”
CLI subcommand routing and flag parsing.

The `SecureBytes` mlock pattern is custom-implemented in
`internal/vault/securebytes/`; the design is inspired by
[sigil](https://github.com/mrz1836/sigil) but takes no dependency on it.


## πŸ“š Documentation

View the comprehensive documentation for hush:

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


## πŸ” 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`](docs/SECURITY.md). For security issues, see our
[Security Policy](.github/SECURITY.md) or contact: [hush@mrz1818.com](mailto:hush@mrz1818.com).


### Additional Documentation & Repository Management

Development Setup (Getting Started)

Install [MAGE-X](https://github.com/mrz1836/go-mage) build tool for development:

```bash
# 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:

```bash
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](https://github.com/goreleaser/goreleaser) for
streamlined binary deployment to GitHub. To get started, install it via:

```bash
brew install goreleaser
```

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

```bash
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/`](.github/workflows/).

Updating Dependencies

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

```bash
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](https://github.com/mrz1836/hush/actions).
View the [configuration file](.github/workflows/fortress.yml).

Run all tests (fast):

```bash
magex test
```

Run all tests with race detector (slower):

```bash
magex test:race
```


### Test Coverage

View coverage report:

```bash
magex test:coverage
```

Coverage reports are automatically uploaded to [Codecov](https://codecov.io/gh/mrz1836/hush)
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:

```bash
magex bench:default time=2s
```

Capture a baseline file for diffs across branches:

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

Compare two runs:

```bash
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](.github/CODE_STANDARDS.md).


## πŸ€– AI Usage & Assistant Guidelines

Read the [AI Usage & Assistant Guidelines](.github/CLAUDE.md) for details on
how AI is used in this project and how to interact with AI assistants.


## πŸ‘₯ Maintainers

| [MrZ](https://github.com/mrz1836) |
|:------------------------------------------------------------------------------------------------:|
| [MrZ](https://github.com/mrz1836) |


## 🀝 Contributing

View the [contributing guidelines](.github/CONTRIBUTING.md) and please follow
the [code of conduct](.github/CODE_OF_CONDUCT.md).

### How can I help?

All kinds of contributions are welcome :raised_hands:!
The most basic way to show your support is to star :star2: the project, or to
raise issues :speech_balloon:.
You can also support this project by [becoming a sponsor on GitHub](https://github.com/sponsors/mrz1836) :clap:
or by making a [**bitcoin donation**](https://mrz1818.com/?tab=tips&utm_source=github&utm_medium=sponsor-link&utm_campaign=hush&utm_term=hush&utm_content=hush)
to ensure this journey continues indefinitely! :rocket:

[![Stars](https://img.shields.io/github/stars/mrz1836/hush?label=Please%20like%20us&style=social)](https://github.com/mrz1836/hush/stargazers)


## πŸ“ License

[![License](https://img.shields.io/github/license/mrz1836/hush.svg?style=flat&v=1)](LICENSE)

This project is licensed under the terms of the [`LICENSE`](LICENSE) file at
the repo root.