Briglia CLI
GitHub
Project

Security & signed releases

Every binary Briglia downloads — the installer, updates, and the phone app — is authenticated with an Ed25519 signature against a key pinned inside the client, before a single byte of it runs. Hosting is not trusted; the signature is.

How a release is authenticated

  1. Releases are published as immutable GitHub Releases of permaevidence/briglia-cli and permaevidence/briglia-ut. Once published, assets cannot be changed or deleted and the tag cannot be reused.
  2. Each release carries a signed envelope, manifest.sig.json: a manifest (version, monotonic sequence number, publication and expiry dates, and for every platform the exact asset URL, size and SHA-256) plus an Ed25519 signature over a domain-separated input — ada-release-envelope-v1 \0 channel \0 keyId \0 payload. (The format name is historical — it predates the project's rename — and is deliberately unchanged: it is part of what every envelope signs.)
  3. Clients verify before trusting. The installer script, the briglia binary (briglia upgrade) and the Ubuntu Touch app each carry the release public key and refuse the release unless: the signature verifies, the format and channel match, the manifest is within its validity window, every asset lives under the release's own GitHub location, and the downloaded bytes match the signed size and SHA-256 exactly (checked while streaming — Content-Length is not trusted, and there is a hard byte bound).
  4. Anti-rollback. The sequence must be at least the minimum baked into the client and at least the highest sequence that device has ever accepted for the same channel — so nobody can feed you an old, vulnerable release even with a valid signature.
  5. The publisher re-verifies its own work. The CI pipeline (CLI) and the local publisher (app) fetch the public release back, authenticate it with the committed public key, and require byte identity with what they signed before declaring the release done. An hourly independent watcher re-verifies both channels and alerts on any mismatch.
What this protects against

A compromised or misconfigured download host, a tampered asset, a forged “latest” manifest, and downgrade attacks. It does not protect against a compromised signing key — see the notes on key custody below — nor is it a substitute for keeping your own machine and provider accounts secure.

Release keys

ChannelKey IDEd25519 public key (raw, hex)
Briglia CLIbriglia-cli-release-v1-94d967bae0867c2e621031636aa2bb2edb64a58f2f72de7bc3559b08d717c79b4251f8b1e35b8a95
Ubuntu Touch appbriglia-ut-release-v1-7bb0163ac16c5cb3cdfa5dba857ad9276f2630c0c7028b53ea9933cc969e69f0a1cff4727ff0b7dc

The same values are pinned in the installer, in the briglia binary, in the app (py/release_verify.py) and in this website's download page. Compare them: if this page and the repositories ever disagree, trust neither and report it.

Renamed from Ada CLI. These are the same two keys that signed every Ada CLI and Ada companion release; only their IDs changed, because an ID is derived from the channel name plus the key's fingerprint (the 94d967… / 7bb016… halves are unchanged). The channels are now briglia-cli and briglia-ut, and the sequence numbers continue rather than restart — the first Briglia CLI release is sequence 60, the first Briglia app release sequence 2 — so no envelope ever exists whose sequence regresses across the rename. Clients from before the rename refuse the new channel by design (see Updates).

Key custody: the private keys never touch the build machines' disks in plaintext outside the signing step — the CLI key lives only in a protected GitHub Actions environment that can be used exclusively by version tags, the app key on the maintainer's machine with mode 0600 and encrypted offline backups. The signed manifests expire (≈180 days) so a stale channel cannot be replayed forever.

Verify a release yourself

You need OpenSSL 3 and python3. This reproduces what the clients do for the Briglia CLI channel:

# 1. Fetch the signed envelope of the latest release
curl -fsSL -o manifest.sig.json \
  https://github.com/permaevidence/briglia-cli/releases/latest/download/manifest.sig.json

# 2. Rebuild the pinned public key as DER (SPKI prefix + raw key)
python3 - <<'PY'
import base64, json, sys
env = json.load(open("manifest.sig.json"))
assert env["format"] == "ada-release-envelope-v1" and env["channel"] == "briglia-cli"
assert env["keyId"] == "briglia-cli-release-v1-94d967bae0867c2e", env["keyId"]
open("pub.der", "wb").write(bytes.fromhex("302a300506032b6570032100" + "621031636aa2bb2edb64a58f2f72de7bc3559b08d717c79b4251f8b1e35b8a95"))
payload = base64.b64decode(env["payload"], validate=True)
open("msg.bin", "wb").write(b"ada-release-envelope-v1\0briglia-cli\0" + env["keyId"].encode() + b"\0" + payload)
open("sig.bin", "wb").write(base64.b64decode(env["signature"], validate=True))
open("manifest.json", "wb").write(payload)
PY

# 3. Verify the Ed25519 signature
openssl pkeyutl -verify -rawin -pubin -keyform DER -inkey pub.der -in msg.bin -sigfile sig.bin
# → "Signature Verified Successfully"

# 4. Read the authenticated manifest, then check an asset against it
python3 -c 'import json; m=json.load(open("manifest.json")); print(m["version"], m["sequence"], m["expires"]); print(m["platforms"]["macos-arm64"])'
curl -fsSL -o briglia-macos-arm64.tar.gz "$(python3 -c 'import json; print(json.load(open("manifest.json"))["platforms"]["macos-arm64"]["url"])')"
shasum -a 256 briglia-macos-arm64.tar.gz   # must equal the sha256 in the manifest; size must match too

For the Ubuntu Touch app use channel briglia-ut, the app key above, the envelope at github.com/permaevidence/briglia-ut/releases/latest/download/manifest.sig.json, and the single click platform entry.

The installer

The one-line installer verifies the same envelope with the same pinned key using the system openssl (probed against an RFC 8032 known vector first, so an unsuitable OpenSSL is never mistaken for a bad signature) and python3, then downloads the version-pinned tarball and enforces its signed size and SHA-256. On a system with neither tool it says so plainly and falls back to HTTPS plus the published SHA-256 sidecar — first-install authenticity then rests on the TLS connection to github.com, and briglia upgrade is fully verified from then on.

Where trust state lives on your device

  • Briglia CLI records the highest accepted sequence per channel and location in ~/.local/share/briglia/release_trust.json, under a cross-process lock, monotonically; a corrupt file is reported and the floor baked into the binary still applies.
  • The Ubuntu Touch app keeps the same floor in ~/.config/briglia-ut/release_trust.json (mode 0600). Settings shows which Ed25519 backend the phone uses — the system OpenSSL, or a built-in verifier; both are proven against the RFC 8032 known-answer vectors before first use.

The agent itself

  • Briglia has a real shell and full access to your user's files, with no approval prompts by design — it must work while you are away. Run it under an account and on a machine you are comfortable entrusting; a dedicated user or a Raspberry Pi is ideal for an always-on setup.
  • Secrets live in ~/.config/briglia/secrets.json (mode 0600, multi-process safe) and are injected into commands only when needed; command output is redacted of known secrets before the model sees it.
  • Text arriving from the outside world (web pages, emails, documents) is marked as untrusted before it reaches the model, and messages you send while Briglia is working are authenticated with per-delivery nonces so a document cannot impersonate you.
  • Backups (.mind archives) restore scheduled shell watchers in a paused, quarantined state; you approve each one explicitly with /resumewatcher.
  • /deleteuserdata quiesces all work, revokes connected email credentials and wipes local data, reporting honestly if anything could not be removed.

Reporting a vulnerability

Please report privately through GitHub's advisory form — Security → Report a vulnerability on briglia-cli or briglia-ut. Don't open a public issue for anything you believe is a security problem. Include the version, platform/channel and reproduction steps; expect an acknowledgement within a few days. Only the latest release is supported: fixes ship as a new release, not as backports. The full policy is in each repository's SECURITY.md.