Governed publishing pipeline and sanitized AI portfolio
This portfolio: private work reaches the public only through a machine-enforced chain of sanitized draft, deterministic privacy scan, explicit human approval and a signed public record anyone can verify, with an assistant that answers only from approved content.
Problem
Most of an engineer's real work lives in private repositories: client, employer and personal. A portfolio has to show the substance of that work without ever leaking a client name, a credential, a personal detail or a proprietary implementation. A public AI assistant that answers questions about the work must never become a route to private material, and it attracts prompt injection, attempts to extract private information and cost abuse. Copy editing by hand does not scale across dozens of repositories, cannot be audited and fails silently.
Goal
Private work reaches the public only through a chain a machine enforces: sanitized draft, deterministic privacy scan, explicit human approval, signed public record, then publication. The public side is physically unable to reach anything private, answers strictly from approved content, fails closed on any error, stays within free-tier limits and publishes an approval record anyone can verify.
My role
Sole designer and builder: product owner, architect and engineer. I wrote the architecture, the threat model and the decision records, the pipeline, the privacy scanner, the approval and signing flow, the ledger, the site, the edge API, the assistant, the build gate, the tests and the operations runbook, directing AI coding agents under a multi-model review process and approving every design decision myself.
Solution
Two halves. A private command-line pipeline discovers candidate work, redacts it deterministically before any language model sees it, drafts structured case-study content, scans the draft with a rule-based privacy guard, waits for my explicit approval bound to the content's hash, signs an append-only ledger entry, and publishes only the approved files to a separate site repository. That repository holds a static site and a small edge API deployed together as one serverless application. Its build refuses to ship unless every content file matches the signed ledger and the site's own code matches a signed manifest, and its assistant retrieves only from an index built from approved content.
The site, the guard, the ledger and signing, the CI gate and the assistant are live; the automated collection and drafting stages and phone approval are being completed.
How it works
A per-repository source policy sets a tier (public, describe-only or never) and the treatments allowed; anything tiered never is read as metadata only, and anything unlisted fails closed.
A discovery pass diffs repository metadata against a committed snapshot and lists new or changed candidates with a reason; notifications carry counts only.
A collector reads one repository at a pinned commit through the hosting API straight into memory (text files only, size-capped, no binaries), and a deterministic redactor replaces secrets, personal data and names on the denylist with role placeholders before any model sees the text.
A drafter turns the redacted digest into schema-validated structured content through an adapter the tier allows, such as a tool-less headless model call or a local model.
The guard, one rule file shared by the workstation, CI and the edge runtime, returns PASS, WARNING or BLOCK with a description and a remediation for every finding. It checks secret shapes with validators, contact details, a keyed-hash denylist (so the list of private names can ship with the site without revealing them), unknown terms and unsupported claims.
Approval signs exactly one ledger entry for exactly one content hash with a key that never leaves its own agent. Today that happens at my terminal; a watcher that takes a short one-time-code reply from my phone, with an email reply as a backup and the first valid reply winning, is designed and being added.
The publisher copies approved files verbatim into the site repository. CI verifies the hash chain, the keys, strict append-only order and the signed code manifest, re-scans every tracked file and the final build output, generates a retrieval index and an approved-term vocabulary, and only then deploys. A live check confirms approved items serve and withdrawn ones are gone.
At request time the assistant applies a body size cap, question cleaning, a signed session token and a bot challenge, a global pause flag, and a daily per-visitor quota keyed by a hashed address whose salt rotates daily, so raw addresses are never stored. A cheap check before any model call answers some questions with a fixed reply and no model call. Otherwise keyword retrieval builds a prompt from approved chunks only, and a provider ladder tries model options in order: each option's health lives in the database, only one request wins the right to retry a failed option, each call's timeout reserves time for later options, and a failing shared key raises one alert rather than many. Every answer passes an output guard, and each turn is signed so a conversation cannot be forged.
Every pipeline action lands in an append-only private audit log, and a status command reports health, open approvals and any ledger entry without a matching private approval record.
Technical challenges
Making the core rule (never private work straight to AI straight to the public) true by construction rather than by prompt: the site repository and runtime hold no credential, binding or route that reaches private material.
One deterministic scanner that behaves identically in three runtimes, with backtracking-safe patterns and fail-closed loading.
Publishing a denylist without publishing the names in it, using keyed hashes over normalised tokens.
Binding a short approval to exactly one content hash with single-use codes and expiry windows.
Publicly verifiable approvals while the approval evidence itself stays private.
Failing closed everywhere in the assistant, and stopping it naming clients or people it has never seen.
Surviving provider outages and free-tier quotas without stampeding a recovering provider.
Rate limiting without storing personal data.
A strict content security policy with no third-party scripts, and scheduling without always-on infrastructure.
Proving all of it with fixture tests, fault injection, adversarial assistant suites and several independent architecture review rounds.
Capabilities
- Candidate discovery with change detection
- Per-source tiers and treatments that fail closed
- Deterministic redaction before any model call
- Structured drafting through interchangeable model adapters
- Deterministic privacy guard with explained findings
- Hash-bound human approval and a signed, hash-chained public approval ledger
- CI gate over the ledger, a signed code set and the build output
- Static portfolio pages and a machine-readable JSON API
- Grounded question-and-answer assistant with guards before and after the model
- Multi-provider fallback with shared health state
- Privacy-preserving quotas and a bot challenge
- Automated accessibility and performance checks
- Owner status report and private audit log
Technology
Impact
The portfolio went live with every public item traceable to a signed approval, and the build refuses to deploy anything that is not. Describing confidential work in public went from a risky manual judgement to a repeatable, auditable process. Model options that followed planted instructions in the adversarial test suite were kept switched off, so only options that held the rules serve visitors.
Skills demonstrated
- Security architecture and threat modelling
- Privacy engineering
- Applied cryptography: signatures, keyed hashing and hash chains
- Deterministic scanner and rule design
- AI governance and human-in-the-loop design
- LLM application security: injection defence, grounding and output filtering
- Edge and serverless engineering
- Resilience design
- Supply-chain integrity and CI/CD gating
- Test design including fault injection
- Accessibility and web performance
- Technical writing
Demo
Live demoOne invented project write-up for an invented rowing club goes from private notes to a signed page: a planted, obviously fake key is swapped out and the draft blocked; the text is revised; a one-time code approves it; it is signed into a small ledger with a key made in your browser; and the verify step passes, then fails when an entry or the page is changed.
A demo here uses only invented names and data.