Sanitization
The package converts complete observable text and plain JSON DTOs to immutable sanitized representations. It does not change executable arguments or native archives. Detectors cannot recognize every secret or prevent adversarial exfiltration through unrelated messages or external agent software.
Release automation builds and packs one verified commit, passes the immutable
tarball between GitHub jobs, and publishes through npm trusted publishing from
the protected npm-production environment. Initial package creation remains
an interactive npm bootstrap gate; CI must fail closed until the package exists.
The public README documents the installed package’s portable, Node, Deno and stdin adapters. The separate specification workflow publishes the canonical contract without publishing npm artifacts or processing user capture data.
Requirements
| ID | Requirement |
|---|---|
| AR-CORE-001 | Text and JSON MUST be scanned before delivery. Results MUST contain only independent immutable values and fixed safe metadata, never raw input, roots, detector diagnostics, fingerprints of secrets, or restoration mappings. |
| AR-CORE-002 | Scoped known values MUST match literally, including overlaps and documented encoded variants. Rotation MUST retain old values until disposal. Registries MUST be bounded at 1,024 values and 1 MiB of secret material. |
| AR-CORE-003 | Sensitive structured fields MUST be removed regardless of value type. Traversal MUST reject cycles, accessors, custom serializers, unsupported objects, malformed Unicode and collisions without executing user code. |
| AR-CORE-004 | Text blocks MUST be bounded at 2 MiB, structured records at 4 MiB and depth at 32. Detector, policy, decoding and size failures MUST return a fixed omission or rejection without raw fallback. |
| AR-PATH-001 | Trusted roots MUST use longest component-boundary matching across POSIX, Windows, UNC and file URLs. Workspace identity and relative path MUST be distinct from display text. Traversal and secret-bearing filenames MUST NOT become resolvable file capabilities. |
| AR-PATH-002 | Private portable views MAY retain home/temp suffixes. Public views MUST remove non-workspace absolute paths. Unregistered paths MUST be explicitly non-resolvable; root aliases MUST be granted explicitly. |
| AR-STREAM-001 | Whole-block buffering MUST emit no text before successful final scanning. Overflow, cancellation and invalid decoding MUST discard the buffer with one omission. Byte partitions MUST yield the same final result. |
| AR-ADAPTER-001 | Secretlint MUST use pinned 13.0.5 rules with an explicit inventory excluding comment suppression. No repository configuration discovery, raw diagnostics, network validation or source logging is permitted. Portable exports MUST report detector coverage honestly. |
| AR-ADAPTER-002 | Environment and dotenv discovery MUST require explicit grants and happen only on an explicit call. Refresh MUST be atomic, bounded and retain rotated values. Node and Deno enforcement MUST be tested separately. |
| AR-ADAPTER-003 | Synchronous durable-store boundaries MAY use an isolated worker and bounded shared result buffer. They MUST use the same complete-block policy, reject executable input objects before worker transfer, enforce a deadline without depending on the caller event loop, and return immutable sanitized data or a fixed omission. |
| AR-ADAPTER-004 | The Node package executable MUST accept one bounded versioned JSON request on stdin, emit only a sanitized result or fixed omission on stdout, keep stderr empty, and never discover policy, credentials or source files implicitly. |
| AR-PKG-001 | Portable exports MUST NOT depend on a runtime, controller, signer, relay, database or native VCS. Published dependencies MUST use registry versions. |
| AR-PKG-002 | GitHub release jobs MUST install the frozen registry lockfile, run the package and specification gates, publish the exact checksum-verified tarball through npm OIDC, and verify registry integrity plus provenance in a clean consumer. Initial npm package creation MUST remain an explicit bootstrap gate. |
| AR-NATIVE-002 | Native releases MUST qualify registry-only dependencies, packaged license and notice files, immutable source/lock/archive checksums, and an isolated archive consumer. Publication MUST require an exact-main manual dispatch, explicit version and checksum approval, successful exact-source CI, and a protected environment. First crate creation MUST use a separately admitted bootstrap credential; subsequent publication SHOULD use short-lived trusted authentication. Existing versions MUST match their recorded checksum and MUST NOT be replaced. |
| AR-NATIVE-001 | The native Rust library MUST retain the Kingfisher 1.4.0 builtin-medium hook and transcript policy identities, exact path/secret replacement, canonical JSON bytes and bounded omission behavior. It MUST import no JJ, controller, runtime or application model. Streaming prefix replacement MUST remain memory-only until the resulting record is scanned. |
Local pnpm check also runs Prettier formatting and counted-line architecture
budgets; see architecture.md.
Known values are byte-sensitive strings, not a blanket entropy heuristic. Commit IDs and public keys are not credentials. Exact required protocol identifiers that would be transformed must be rejected by the publishing consumer. Native archives and external ACP provider/session files are separate boundaries.
Public output uses $WORKSPACE[id]/path, [REDACTED], and [EXTERNAL_PATH].
Private portable output additionally permits $HOME and $TMP. These marker
strings are reserved; a known value colliding with them invalidates the scope.
No mapping is exported with the result. Encoded matching covers URI component,
JSON-escaped, UTF-8 base64 and base64url representations, not arbitrary encodings.
Re-exporting a private portable view under public policy MUST remove $HOME and
$TMP suffixes as well as original non-workspace roots. Synchronous boundaries
expose the same structured LogicalPath classification as asynchronous scopes.
The synchronous adapter reuses one bounded result buffer per scope; repeated
small records MUST NOT allocate an additional maximum-sized buffer per call.
The enforcement adapter MUST disable Secretlint profiling through its public API
before scanning, so rule timing entries do not grow with private record traffic.
Only complete text and plain data objects are supported. Binary attachments and opaque transport data must be represented by an omission by the caller. No incremental release guarantee is claimed. A scanner deadline may omit a block; Node enforcement isolates scanning in a worker so it can terminate the scan.
The lapismd-agent-redaction executable is a transport adapter around the same
Node scope. Its version-1 request carries one JSON value and optional explicit
secrets, roots and destination policy. Input is capped at six MiB so a
four-MiB record plus bounded policy can be admitted without incremental output.
Malformed, oversized, unsupported or detector-failed input produces only the
fixed omission contract; source data and diagnostics never reach stderr.
Network URL tokens, including WebSocket relay URLs, are excluded from filesystem path matching. Destination metadata policy remains consumer-owned.
Native Rust policy
lapis-agent-redaction exposes the preserved native Kingfisher hook and
transcript profiles independently of the TypeScript Secretlint policy. Native
root and exact-secret prefix replacement emits only through a subsequent
complete-record scan; replacement output alone is not a sanitized value.
Hosts supply an admitted private compiled-rule cache. The library owns no
repository lookup, runtime launch, credential discovery or durable transcript.
| ID | Requirement |
|---|---|
| AR-NATIVE-001 | The native Rust library MUST retain the Kingfisher 1.4.0 builtin-medium hook and transcript policy identities, exact path/secret replacement, canonical JSON bytes and bounded omission behavior. It MUST import no JJ, controller, runtime or application model. Streaming prefix replacement MUST remain memory-only until the resulting record is scanned. |
Native release qualification
The native 0.1.0 candidate and npm 0.1.1 metadata update are independently
approved publications. The TypeScript runtime policy is unchanged. Native CI
uses Rust 1.99.0 on Linux and Apple Silicon macOS, checks the packaged Cargo
archive, and runs canonical-byte/detector/omission regressions in a temporary
consumer outside the checkout. Before publication that consumer uses only the
extracted candidate plus registry dependencies; after publication it resolves the
exact public crate with no path override. Neither mode imports sibling code.
The manual Cargo workflow defaults to qualification without writes. An approved
publish dispatch binds the source SHA, crate version and archive SHA-256 and
rechecks current main plus its successful CI. First publication uses the
protected crates-io-bootstrap environment; trusted subsequent publication uses
crates-io. Credentials are never printed or stored in artifacts. Uncertain
uploads are not retried: inspect public identity and checksum, then resume only
verification or missing work. Tags and GitHub releases require their own explicit
release step after registry and consumer verification; source CI creates neither.
Native detector test credentials are synthetic and assembled at runtime; the complete credential-shaped fixture is not stored as a literal in source. This preserves detector acceptance without a public source-scanning exception. Formatting that fixture preserves AR-NATIVE-001 and AR-NATIVE-002 unchanged; native qualification still runs both complete privacy regressions.
The approved 2026-10-20 publications complete AR-NATIVE-002’s public-byte and registry-consumer evidence. AR-NATIVE-001/002 and AR-PKG-001/002 remain unchanged: native and TypeScript policy stay independent, and source, checksum, approval, protected authentication and consumer gates remain required. The publication record retains both packages’ immutable source identities, failed attempts and recovery. Linux and macOS ARM64 are qualified; other native platforms and downstream application adoption remain outside this library release. Evidence-only changes do not republish a package.
Architecture
Repository-owned source validation enforces counted-line budgets so modules
stay extractable. Counted lines are physical lines minus blanks and
comment-only lines, measured by scripts/architecture-lines.mjs. New
production and script files are capped at 300 counted lines and new tests at 500. Existing paths use scripts/architecture-baseline.json as a no-growth
ratchet: growth requires an updated budget in the same change, and a budget
more than twenty counted lines above the current size is stale.
Generic dump filenames (utils, helpers, common, services) and
production imports of tests are rejected. src/types.ts remains a cohesive
public-contract module. Fixture trees under scripts/architecture-fixtures/
are harness-only and stay outside TypeScript include.
Requirements
| ID | Requirement |
|---|---|
| AR-ARCH-001 | Repository validation MUST enforce counted-line budgets with scripts/check-architecture.mjs. New production and script source MUST NOT exceed 300 counted lines, and new tests MUST NOT exceed 500. Existing files MUST NOT increase counted lines without updating the path budget in scripts/architecture-baseline.json in the same change. A recorded budget MUST fail when the current counted size plus twenty is still below that budget. |
The Rust crate sources and integration tests use the same counted-line limits. Cargo build output is excluded. Native privacy is independent of the TypeScript adapters and does not alter their complete-block streaming contract.
Specification governance
Requirements
The public README uses only declared package exports. The independent
publish-spec-pages.yml workflow builds the canonical book without installing
runtime dependencies, so spec publication does not depend on npm release gates.
| ID | Requirement |
|---|---|
| AR-GOV-001 | Canonical chapters MUST be listed in SUMMARY.md and build with mdBook. Generated output MUST be ignored. |
| AR-GOV-002 | Protected changes MUST update the mapped canonical chapter before or with code. |
| AR-GOV-003 | Every requirement MUST have exactly one verification row distinguishing implemented and planned evidence. |
| AR-GOV-004 | Repository validation MUST include Prettier formatting through pnpm check:format. The check MUST use the repository Prettier configuration and MUST fail on unformatted tracked source. |
| AR-GOV-005 | The README MUST document registry installation, public runtime imports, safe result handling, explicit secret grants, disposal, complete-block capture, and the published specification. |
| AR-GOV-006 | GitHub Actions MUST build canonical spec/src with mdBook on specification pull requests and publish the generated book to GitHub Pages only from main, including manual dispatch. Generated output MUST remain untracked. |
Change map
| Protected paths | Canonical chapter |
|---|---|
src/, scripts/check-boundaries.mjs, scripts/deno.test.ts, package.json, deno.json, docs/ | sanitization.md |
scripts/check-architecture.mjs, scripts/architecture-*, scripts/architecture-fixtures/ | architecture.md |
AGENTS.md, spec-validator.config.mjs, spec/book.toml, .gitignore, .prettierrc.json, .prettierignore | spec-governance.md |
Release helpers, workflow files, and their package scripts map to sanitization and AR-PKG-002 / AR-NATIVE-002. Registry lockfile audit updates and the workspace override map to this governance chapter. The release checks retain all package assertions.
Rust crates/, Cargo.toml, Cargo.lock and rust-toolchain.toml changes map to
sanitization.md; native package ignores and validator mappings map here.
The native privacy test configuration includes its dependency-boundary regressions without changing the existing TypeScript and architecture scenarios.
Wave 2 reviews all open Dependabot proposals before source publication. No proposals were open on 2026-10-20; the frozen npm audit has zero findings. Native detector pins remain policy identities and are not upgraded as routine tooling.
Verification
| ID | Status | Evidence |
|---|---|---|
| AR-CORE-001 | Implemented | src/sanitizer.test.ts; src/corpus.test.ts |
| AR-CORE-002 | Implemented | src/corpus.test.ts literal, overlap, encoded and registry cases |
| AR-CORE-003 | Implemented | src/corpus.test.ts adversarial plain DTOs |
| AR-CORE-004 | Implemented | src/corpus.test.ts; src/adapters.test.ts worker timeout |
| AR-PATH-001 | Implemented | src/corpus.test.ts portable paths |
| AR-PATH-002 | Implemented | src/corpus.test.ts private/public/external paths, isolation and network URL exclusion |
| AR-STREAM-001 | Implemented | src/sanitizer.test.ts every UTF-8 split; src/corpus.test.ts cancellation |
| AR-ADAPTER-001 | Implemented | src/adapters.test.ts; scripts/deno.test.ts |
| AR-ADAPTER-002 | Implemented | src/adapters.test.ts grants and rotation; scripts/deno.test.ts |
| AR-ADAPTER-003 | Implemented | src/sync.test.ts first-write and deadline checks; scripts/deno.test.ts |
| AR-ADAPTER-004 | Implemented | src/stdio.test.ts bounded stdin, sanitization and secret-free process output |
| AR-PKG-001 | Implemented | scripts/check-boundaries.mjs; pnpm publint |
| AR-PKG-002 | Implemented | scripts/npm-release.test.mjs; .github/workflows/ci.yml; .github/workflows/release.yml; pnpm test:release; npm-production artifact checksum and registry provenance gates |
| AR-ARCH-001 | Implemented | scripts/architecture.test.mjs; pnpm check:architecture |
| AR-GOV-001 | Implemented | pnpm spec:check |
| AR-GOV-002 | Implemented | pnpm spec:check |
| AR-GOV-003 | Implemented | pnpm spec:check |
| AR-GOV-004 | Implemented | pnpm check:format |
| AR-GOV-005 | Implemented | README.md; public-export example compilation; package checks |
| AR-GOV-006 | Implemented | .github/workflows/publish-spec-pages.yml; mdbook build spec; actionlint |
| AR-NATIVE-001 | Implemented | crates/lapis-agent-redaction/tests/native_privacy.rs; spec/records/native-rust-extraction.md; jj-store transcript_sanitization compatibility |
| AR-NATIVE-002 | Implemented | scripts/native-release.test.mjs; .github/workflows/qualify-native.yml; published bytes, protected OIDC and registry consumers |