DRAGON/v1.4.0/SECURITY

Security and compliance posture

DRAGON is built for environments where session data cannot leave the room: SCIFs, the tactical edge, OT and ICS networks, and classified and air-gapped facilities. The security posture follows from that constraint. This page is the customer-facing statement; the mechanisms behind it are documented in the subsystem references.

The formal threat model is a roadmap deliverable. Until it lands, this page is a posture statement, not an assurance argument. Known gaps are stated plainly below.

No telemetry#

DRAGON sends no telemetry by default — no usage analytics, no crash reports, no phone-home. Any future telemetry will be opt-in, documented in full, and forcibly disabled in offline mode.

Offline mode as a provable claim#

In offline operation DRAGON makes zero outbound network connections except the device transports the operator explicitly opens. Inference runs on the embedded llama-server child process over loopback, embeddings and the vector store are local, and licensing verifies signed license strings entirely on-device with no entitlement callbacks — the trial itself starts and runs without a network. This is a testable claim, not a marketing one: the audit log's net_egress records are the evidence, capturing every outbound connection the daemon makes.

Air-gapped installs update by installing newer signed installers by hand, verified against the published SHA256SUMS manifest. They never contact the update endpoint.

Two-stage redaction before any model contact#

Secrets are scrubbed at two independent stages — at RAG ingestion before text is embedded, and at context assembly before any model call. Coverage spans Cisco type 0, 5, 7, 8, and 9 secrets, enable secret, SNMP communities, TACACS+ and RADIUS keys, IKE PSKs, SSH private-key blocks, password echoes, and a high-entropy heuristic; type 7 is redacted, never decoded. Replacements preserve analytic utility, and every redaction event is logged by class, location, and time, never the secret itself.

A stricter ruleset auto-applies when the inference target is a remote endpoint. Since v1.4.0, RAG ingestion always runs the strict ruleset regardless of endpoint — nothing embedded to disk or sent to an embeddings endpoint can carry a bare high-entropy secret — and the high-entropy pass scans every line, including lines a structural rule already touched, so a second secret beside a caught one cannot slip through. Both were escapes found by an adversarial review of v1.3.0 and are now regression-gated. The redaction test corpus is its own named CI job, and a redaction escape is a release blocker. See redaction and the audit log.

Auditable AI, with provenance#

Every suggestion, acceptance, dismissal, redaction event, model invocation, and RAG retrieval is recorded in an append-only, hash-chained audit log — including the verbatim post-redaction prompt sent on every model call. fs_access and net_egress evidence records tie private-key reads and outbound connections to the originating user action and OS account, an in-product transparency pane sits over the live log, one-click export writes the complete log as canonical JSONL or CSV, and configurable audit storage lets the log be written to a WORM or classified volume, fail-closed. The format is a public artifact, so customers verify and ingest it with their own tooling using only SHA-256.

AI usage honors the license: an expired trial or license stops all model calls — manual, ambient, and auto-analyze — and the network egress and API cost they incur.

Suggested commands are never auto-executed. Acceptance places a command in the input line for the operator to send. No setting overrides this. It is a product invariant, with exactly one audited exception, below — and that exception is bounded by who initiated the run, never by anything a model produced.

The read-only auto-execute gate#

Collection Runs are the one place the core transmits commands without a human keypress, and the crossing is deliberately narrow. The executor auto-sends only commands a rules-only classification guard proves read-only — profile rules, then a conservative built-in ruleset, then a reviewed Linux read-only allowlist. Anything else, including any command the classifier does not recognize, is skipped and never transmitted. Every auto-sent and skipped command is written to the audit log as an auto_exec entry with its classification and outcome. The executor is a single choke point — one tested, audited code path rather than execution scattered through the product.

With user-authored sets in v1.3.0, the gate was hardened against smuggled writes: every command a set could auto-run is re-classified at run time; every ingress of user-authored command text rejects in-band control characters (a CR/LF, backspace, Ctrl-U, or ESC that would make the device execute a different command than the one classified), and the executor itself refuses a control-bearing line as a final backstop; and a read-only-classified command carrying redirection, chaining, substitution, or a pipe into anything but a network read-only filter (show > /etc/passwd, ping | tee …) is downgraded and skipped. Snapshot output that enters the copilot's context is redacted, and only output the operator explicitly opted in is ever shown to the model.

Operator mode#

v1.4.0 refines the gate's framing rather than reversing it: the read-only rule exists to stop an AI from transmitting a config or destructive command on its own, and it stays hard for every AI-drafted set and every automatic caller. A human who authored and reviewed a set may mark it operator mode, and that set then runs all of its steps — config and destructive included — the way every incumbent terminal already trusts an operator's own script. The authorization is per-set, taken verbatim from the editor at save time, never inferable from model output; an AI-generated set cannot carry it, and a duplicate does not inherit it. Every step is still classified, badged, and written to the audit log as auto_exec with its class and outcome, a banner shows the mode before the run, and the control-character backstop above is unconditional in both modes. Against a local shell, an operator-mode set executes on the operator's own workstation — a blast radius set authors must weigh.

Automatic session reconnect is held to the same standard: a background re-dial happens without a keypress, so every attempt and outcome is an audited reconnect entry, bounded by backoff and a give-up window — never an invisible infinite loop.

Credentials#

Credentials — session passwords, Vault credentials and key passphrases, and AI API keys — are stored exclusively in the OS keychain (the Windows credential store, macOS Keychain, or Linux Secret Service), never plaintext on disk. The application store keeps only an opaque keychain reference. The wire protocol reports credential presence but never returns a stored secret, private keys referenced by path are read only by the daemon at dial time, and audit payloads carry presence flags, never key material.

An in-app factory reset (Settings → Security & Audit) wipes all DRAGON state on every platform — license, saved sessions, known hosts, Vault credentials, provider keys, RAG index, and audit log. Every secret is destroyed; the only survivor is the trial anti-abuse marker, which holds no secret and no personal data. See licensing.

Local daemon hardening#

The core runs as a loopback-bound WebSocket daemon. Because loopback binding alone does not stop a malicious web page via DNS rebinding, the daemon adds layered defenses:

  • A loopback-literal Host header requirement and an Origin allow-list, defeating DNS-rebinding and cross-origin drive-by connections.
  • A per-launch connection token, generated by the shell from the OS CSPRNG on every start and carried as a WebSocket subprotocol so it never appears in a URL, log, or process list. The comparison is constant-time. The token is additive to — never a replacement for — the Host and Origin checks.
  • A concurrent-session cap and per-connection rate limiting on expensive operations, a resource-exhaustion defense.
  • RAG ingestion confined to explicitly allowed, symlink-resolved roots and volume-capped per walk, a path-traversal and resource defense.
  • Bounded buffers on all untrusted input — capture line and output size, PDF extraction time — so a hostile or malfunctioning device cannot exhaust memory. Dropped internal events are counted and logged, never silent.
  • Local shells are spawned only by an ID the daemon itself discovered on disk; the WebSocket surface cannot name an executable path. A local spawn is audited as net_egress with locality local, so it never reads as network egress in the zero-outbound evidence.
  • The render path is isolated from the UI: every window drains its own bounded queue, and durable persistence has its own event subscription, so a hung webview can neither stall a live session nor drop a command record from the audit trail.

These controls were added in response to an internal multi-agent security audit; its findings — one critical, six high, six medium, five low — were all remediated, and each redaction-pattern fix is CI-regression-gated. A separate memory audit drove the RAG ingestion bounds. A second adversarial review, against v1.3.0, filed two critical and eight high findings; v1.4.0 closes both criticals (the redaction escapes above) and the high-severity findings on the render path and Collection Runs. The remaining findings — licensing tier enforcement, trial fail-open, audit-chain anchoring, RAG root confinement, and pre-scrub audit excerpts — are tracked openly and listed under known gaps.

Host-key trust#

SSH host keys use trust-on-first-use against a strict known_hosts. An unknown host prompts the UI with the SHA-256 fingerprint and a 60-second timeout. Since v1.4.0 a known host presenting a changed key raises a distinct changed-key prompt carrying both the stored and the presented fingerprint, so a legitimate migration or reinstall can be accepted after an out-of-band check; accepting replaces the stored entry atomically, and a multi-host line keeps its other hosts. With no UI to answer, either prompt is rejected. Every verdict — including every accepted key change — is audit-logged, and known_hosts writes are serialized so concurrent dials cannot lose a pin or corrupt the file.

Supply chain and distribution#

Every release attaches SBOMs in both SPDX and CycloneDX form covering the Go core, the frontend and shell npm trees, and the Rust shell, plus a SHA256SUMS integrity manifest regenerated over the final artifact set. A release gate verifies artifact completeness before anything publishes. Dependency vulnerability checks run on every build — govulncheck for the Go core and an npm audit posture for the frontend. The core is CGO-free, so cross-compilation needs no per-target C toolchain.

Update signing is mandatory: releases cannot build without the updater signing key, and installs verify every update against the baked-in public key. OS code signing — Windows Authenticode via Azure Trusted Signing and Apple Developer ID with notarization — is wired into the pipeline and activates when the certificates are configured; see the honest-gaps note below.

Known gaps, stated honestly#

The posture is documented with its gaps visible:

  • The IPC is still loopback TCP, not an OS named pipe or Unix domain socket. The per-launch token mitigates this; the migration remains a tracked hardening item.
  • The formal threat model is pending. Assets in scope: credentials, session data, RAG corpora, license keys. Adversaries in scope: endpoint malware, insider exfiltration via remote AI endpoints, supply-chain compromise.
  • FIPS-validated crypto is tracked as a roadmap item for federal procurements that require it.
  • Audit-log tail-checkpoint signing is a roadmap item; the chain proves integrity and order, not authorship. Custom audit storage onto WORM media is the current mitigation.
  • Licensing does not yet enforce edition tiers or reject a missing trial ledger, the default RAG root is broad, and retrieval audit entries can carry pre-scrub excerpts — all filed by the v1.3.0 adversarial review and scheduled for the next release.
  • The trial anti-abuse hardening stops casual resets — factory reset, uninstall and reinstall, data-directory deletion, and clock rollback — not a determined adversary. A user who deletes the out-of-data-dir marker itself, forges its integrity tag from the shipped binary, or runs under a second OS account still obtains a fresh trial. A server-issued trial license is the tracked durable fix.
  • OS code-signing certificate procurement is in progress. Builds produced without the certificates configured ship unsigned and will trip OS trust prompts such as Windows SmartScreen and macOS Gatekeeper; the updater signature check applies regardless.

Reporting#

Send security reports to security@ethrx.io. Customers are encouraged to submit redaction-escape fixtures; fixtures are append-only, so a fixed escape can never regress.