Repository navigation
Conversation
Starts Discreet's development for Milestone 2: - Go 1.27 module and the `discreet` command with serve, audit verify, audit export and version. Each describes itself until its issue builds it; run() takes its arguments and output streams so tests need no subprocess. 12 table-driven tests. - `make check` runs gofmt, go vet, go test -race, govulncheck (pinned v1.8.0) and a build, locally and in CI. CodeQL covers Go and the workflows. Every Action is pinned by commit SHA; Dependabot targets next. - Evidence files with honest "not measured yet" content: CONTROLS (v0, mappings pending D11), THREAT_MODEL, EVALS, RISKS, CHANGELOG and the build log, plus ADR 0001 (standard library only) and ADR 0002 (same integration-branch workflow as Vetted). Closes makoydev/vetted#30 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- internal/detect implements the sg-pii-rules spec with Go's regexp (RE2): validators written from VALIDATORS.md (NRIC/FIN incl. M series, NRIC look-alikes, Luhn + card network prefix, postal sector, calendar date), value groups, and the overlap rule. Fails loudly on an unknown validator or a non-RE2 pattern. Offsets are bytes. - sg-pii-rules v0.2.0-rc.1 (9d40723) vendored by scripts/vendor-sg-pii-rules.sh into third_party/sgpiirules and embedded with go:embed; a test re-checks every file against SHA256SUMS and that nothing unlisted is vendored (ADR 0003). - Conformance: 369/369 shared cases pass, first run. - Deliberate breaks, all restored from backup: wrong M-series table (2 fail), Luhn always true (3), value groups ignored (52), hand-edited fixture (1). A reversed tie-break failed nothing: no shared case has two detectors on the same span. Added toy-detector overlap tests (3 now fail) and opened makoydev/vetted#44 for the shared suite. - About 3.9 MB/s on dense synthetic text (BenchmarkDetect). Closes makoydev/vetted#33 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- internal/protect: a session per request numbers placeholders per entity (<NRIC_1>), reuses them for the same value across messages, remembers values only by a keyed hash, redacts as [REDACTED_CARD] or refuses with BlockedError, following a strictly parsed YAML policy. Restore only replaces placeholders the same session issued. - internal/vault: AES-256-GCM in memory under a per-process random key, each value bound to its key, deleted on Close, expired after 10 minutes; prints as redacted. - Default policy: tokenise everything except CARD (redact); denied purposes listed for D5. - go.yaml.in/yaml/v3 v3.0.5 (maintained fork; gopkg.in/yaml.v3 is archived), the one module ADR 0001 allowed. ADR 0004. - Deliberate breaks: session-less vault keys, no placeholder reuse, redact restored; each failed a test; restored from backup. Closes makoydev/vetted#34 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- internal/audit: append-only JSON lines; each record holds the SHA-256 of the previous one and HMAC-SHA-256 of prompt and response (key from DISCREET_HMAC_KEY, at least 32 characters), never the text. Writes are fsynced. Open verifies an existing log and refuses to append to a broken chain. Cost per UTC day is tracked for the D6 budget. - discreet audit verify names the first changed, missing or reordered record (no key needed); discreet audit export -csv exports a verified log with spreadsheet formulas neutralised. - Tests: changed byte, deleted record, swapped records, a rewritten record with its own hash recomputed, reopening, 50 concurrent appends, CSV. Deliberate breaks (no hash check, no link check, appending to a broken log) each failed tests; restored from backup. - ADR 0005, including the limits: a full chain rewrite or a truncated tail can't be detected without an external copy of the head hash. Closes makoydev/vetted#37 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- discreet serve: OpenAI-compatible POST /v1/chat/completions (non-streaming) and /healthz. Messages are protected in one session, sent to the model as placeholders, restored in the answer, which ends with a disclosure footer. Every request, allowed or not, is recorded; no answer without an audit record. Refuses to start without DISCREET_HMAC_KEY or on a broken audit log; graceful shutdown. - X-Discreet-Purpose required; automated eligibility / benefits decisions refused with 403 before the model is called (ADR 0006). Purpose stored scrubbed of personal data. - Only model, messages, token limit and temperature are forwarded; tools, images, tool roles and streaming are rejected. - Mock model (default, free) drafts a reply that reuses placeholders. - Checked with OpenAI's official Python library v3.24.0, changing only base_url. Tests include a scan of every log line for planted values. - Deliberate breaks (original text forwarded, purpose check skipped, prompt logged) each failed tests; restored from backup. - Two AI mistakes logged: misleading mock wording; a test that searched JSON-escaped text. Closes makoydev/vetted#35 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
) - upstream.OpenAICompatible: OpenAI or Ollama /chat/completions, configurable token-limit parameter, optional reasoning effort; only the provider's HTTP status is ever reported, never its body. - Access tokens (tenant:token, 24+ chars) kept as SHA-256, compared in constant time. A missing or wrong token gets the free mock, so the public demo can't spend money; the caller's token is never forwarded. - One pinned model with checked prices (gpt-6-luna US$0.10/0.50 per M, OpenAI model page). Each real request reserves its worst case (at most US$0.0076) against a hard daily budget (default US$0.20) summed from the audit log, so restarts can't reset it; refusals are recorded. - 30 requests/minute per client address; proxy header only if named. - Tests use a fake OpenAI server: no paid API is called. Deliberate breaks of tokens (redone so it compiles), budget and rate limit each failed tests; restored from backup. ADR 0007. Closes makoydev/vetted#36 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- GET / serves an embedded page: what the app sent, what the AI saw, what it answered, what the app got back, and the audit record, with five made-up scenarios including a refused eligibility decision. - POST /demo/run runs the same pipeline as /v1/chat/completions: the gateway was refactored so both call one process() function, and every existing gateway test passed unchanged (ADR 0008). The demo always uses the mock model, even with a valid token (tested against a fake paid provider: zero calls). - Strict CSP (own scripts only, connect-src 'self'), nosniff, no referrer; user text inserted as text. Checked in headless Chrome at desktop and phone width, light and dark: no CSP violations. - Session.RestoreParts lets pane 4 highlight restored values; redacted markers stay highlighted as never restored. Closes makoydev/vetted#38 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- internal/bench: a seeded generator written independently of the rules (own NRIC checksum and Luhn, own templates) producing 5,000 samples with 0-3 pieces of personal data in realistic styles, some the rules don't support, plus look-alike decoys. Scored per entity and per writing style; "caught" means fully covered by the same kind. - cmd/benchmark (make bench) prints Markdown: 76.5% recall overall, 100% on every supported style, 92.3% precision (482 false alarms, all bare 8-digit numbers as PHONE, the trade-off in sg-pii-rules ADR 0007). - Latency vs a baseline without Discreet: about 3 ms added at p50, of which 2.9 ms is the audit record flushed to disk and 0.08 ms detection. - Deliberate break (seed ignored) failed the determinism test; restored. Closes makoydev/vetted#39 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Dockerfile: static Go binary on distroless static-debian13:nonroot, non-root, /data owned by that user; both base images pinned by digest. Built and run locally the way Fly will run it: 9.97 MB, placeholders only to the model, audit verify OK on the volume, no values in logs. - CI job "Container image" builds and smoke-tests it on every PR. - fly.toml: Singapore, 256 MB shared machine that stops when idle, 1 GB volume, /healthz check, Fly-Client-IP for rate limits. Mock only: no upstream is ever configured (ADR 0009). - deploy.yml: from main only, skips without FLY_API_TOKEN, pinned flyctl action, smoke test against the live URL. - docs/DEPLOY.md: the one-time steps only Michael can do. README: live link (once deployed), three-minute demo path, five-minute quickstart. - Build log: merge-helper race (refused safely) and a zsh echo slip. Part of makoydev/vetted#40 (deployment waits for Michael's Fly account) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- CONTROLS.md v0.1: eleven controls mapped to section-level primary sources (IMDA MGF for GenAI and Agentic AI, PDPA ss.24 and 26, PDPC AI and NRIC guidelines, OWASP LLM Top 10 2025, CSA, NIST SSDF), reusing the citations read for Vetted's matrix and saying so; gaps listed. - THREAT_MODEL.md reviewed for v0.1 with accepted residual risks, including that Vetted's pilot still uses sg-pii-rules v0.1.0. - RISKS.md updated; docs/CV-NUMBERS.md; build log summary for M2. Part of makoydev/vetted#41 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…16) Michael reviewed the Milestone 2 batch on 2026-10-06 and accepted all nine ADRs without changes. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Reviewed and approved by Michael Mendoza on 2026-10-06 ("everything is approve"); merged by Claude Code at his instruction. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For Michael. Merge with rebase merge so each change stays a separate commit. Decision report: https://makoydev.github.io/discreet/m2-review/ (local copy:
ai-projects/M2-REVIEW.html).What's in it (10 changes, each its own PR, CI-green, merged into
nextby Claude Code withai-merged)audit verify, CSV exportHow to review (about 30 minutes)
docker build -t discreet . && docker run -p 8080:8080 -e DISCREET_HMAC_KEY=$(openssl rand -hex 32) discreet, open http://localhost:8080 and click through the five examples.EVALS.md(benchmark and latency) andTHREAT_MODEL.md(accepted risks).next, and the deploy workflow runs onceFLY_API_TOKENexists.Not included: #13 (V1, pilot to opt-in) waits for 14 Oct. Merging this into
mainrunsdeploy.yml, which skips itself until the Fly.io token exists.🤖 Generated with Claude Code