think
16px
820px

Stego Engine — Feasibility, Readiness & Confidence Assessment

Scope: the deferred combined steganography engine — two orthogonal geometric
channels exposed as three modes (Traceability / Authenticity / Hybrid) — sold as
the premium watermarking module. Priorities, in order: security → consistency →
compatibility → speed
. Dev time/cost is explicitly not a constraint.

This is an assessment, not a build. Verdict first, then the reasoning, the real hard
parts, the risks, and a de-risking plan. Written 2026-07-10.


0. TL;DR verdict

Feasible and well-positioned to build, with high confidence on the architecture and
the invisible/crop-resilient path, and medium confidence on the camera/print-scan path
until a real-capture corpus proves the robustness numbers.

The reason confidence is this high before a line of engine code exists: the hard product
decisions are already made and frozen in a way that can't drift. The seam is done. What's
left is a bounded, well-understood compute problem (font-aware PDF surgery + CV decode) that
has two working reference implementations to clean-room from. This is not research; it is
disciplined engineering against a spec, with one genuine research-grade risk (print-scan
survival) that must be measured, not assumed.

One blocker to resolve before building: the frozen contract has an inverted starmap
comment (§4.1) that, taken literally, would make the engine build the vault for the wrong
mode. Fix the contract first.

Dimension Confidence One-line basis
Architecture / seam readiness Very high Interface, proto, ledger, module-gate all built and frozen
Channel V (Traceability, invisible, crop/screenshot) High Established technique; reference impl exists; blind decode
Channel K (Authenticity, camera) Medium-High Reference exists but has a known decoder-collapse bug to fix + recalibrate
Print-scan survival Medium Design target, not proven — needs corpus + calibration
Font compatibility (simple + composite in one layer) Medium-High The keystone; both ref impls each did only half — must unify
Security / crypto (MAC payload, KMS, mTLS, vault) High Design is sound and standard; replaces the CRC false-positive risk
Collusion / reflow / re-typeset resistance Low (out of scope) Explicitly un-covered gaps; roadmap items, not guarantees

1. What "3 modes" actually is (and why that framing matters)

There are two channels and three modes. The modes are policy presets over the channels:

Mode Channels Axis / mechanism Optimized for Cost
Traceability V only Vertical baseline / line-spacing shift (±~0.7 pt) Invisible, blind self-service verify, survives crop/screenshot/B&W/JPEG No camera resilience
Authenticity K only Horizontal inter-word kern (one-sided +ε, ~1.5–2 pt) Phone-camera capture, strong crypto serial Slightly visible; needs vault
Hybrid V + K Both, orthogonal axes Full capture coverage + cross-channel corroboration Visible (K) + vault + ~2× compute

Why the orthogonality is the whole game. V is read as a width-integrated row centroid
(moving ink horizontally doesn't change it). K is read as per-line gap ratios re-segmented
per line band
(shifting a line vertically doesn't change them). So both signals live on the
same document with near-zero cross-talk — Hybrid is genuinely V and K decoded independently,
not a compromise blend. This is the single most important technical claim, and it's the one
I'd validate empirically first (§6, DR-1) because everything about Hybrid rests on it.

Framing consequence for selling it as a module: the three "modes" are a clean pricing and
policy story (invisible-and-cheap → visible-and-camera-proof → both), and they map 1:1 onto the
already-frozen Mode enum. No new surface area is needed to expose them — the module boundary
already exists.


2. Readiness — what already exists (this is the good news)

The engine is deferred behind a no-op seam that is fully built. Concretely, on disk today:

  • Frozen gRPC contractproto/stego/v1/stego.proto: Encode/Decode/Capabilities/
    Health, the Mode/Tier/Verdict/EgressChannel enums, starmap blob, trace-propagation
    convention (traceparent + issuance-id metadata). Committed and buf-compiled so the Go side
    and the future sidecar cannot drift.
  • Go consumer port + no-op adapterinternal/protection/app/ports.go (the
    ProtectionEngine interface, a field-for-field image of the proto) and
    adapters/noop.go (passes PDF through, Applied:false, Reason:ENGINE_NOT_ENABLED). Swapping
    PROTECTION_ENGINE=grpc is designed to require zero caller changes.
  • Attribution plumbing already live — the Deliverer chokepoint mints the issuance-ledger
    row today, under the no-op engine. So the identity→document→version→mode→key_epoch mapping
    exists before the engine that consumes it. This de-risks integration enormously: the engine
    plugs into a socket that's already wired and load-bearing.
  • Module gating already builtLICENSING.md defines watermarking as a premium module
    unlocked only by a signed license file (requireModule("watermarking"), modules[] in a
    Ed25519-signed license, hot-swappable, air-gap friendly). Note it says "watermarking has no
    routes yet"
    — so the verify/forensic endpoints are net-new surface to add, but the enforcement
    mechanism they'll hang off is done.
  • Reference implementations on disk~/remote-development/Starfield (Channel K / kerning)
    and ~/remote-development/seeddms60x (the DMS host). geomstego (Channel V) behavior is
    documented in the blueprint. These are clean-room design references only (§13 rules): study
    behavior, reimplement, never port. Starfield is covered by the CEO grant.
  • Vetted AGPL-free stack — pikepdf (qpdf, MPL-2.0) for token-level content-stream surgery +
    pypdfium2 (PDFium, Apache/BSD) for glyph boxes and raster render, OpenCV/numpy, tesseract,
    reedsolo. This deliberately removes PyMuPDF/fitz and Ghostscript (the two AGPL landmines) with
    no functional loss. Python 3.11 is present; none of these are installed yet (expected — the
    sidecar doesn't exist).

Net readiness: everything except the compute core itself is in place. There is no
architectural discovery left to do. That is why this reads as an execution task, not an R&D bet.


3. The real hard parts (where the effort and the risk actually are)

Four things are genuinely hard. Everything else is plumbing.

3.1 The unified font-aware surgery layer — the keystone

Both reference impls each handled only one font class: geomstego did Type0/CID only, Starfield
did simple Latin-1/WinAnsi only. That mutual exclusion is an implementation artifact, not a
signal limitation
— but unifying it is the hardest single piece. The one layer must, for the
same text run and both simple (Type1/TrueType/WinAnsi) and composite (Type0/CID) fonts:
1. Resolve each font's encoding/CMap (bytes-per-code + code→Unicode) to find U+0020 spaces and split
TJ/Tj strings at correct code boundaries (1-byte simple and 2-byte CID).
2. Read per-font advance widths (CID W / simple Widths) to convert a Δpt into a TJ kern number
(-1000·Δpt/fontsize, font-independent math).
3. Track full text/graphics state (Tm, Tlm, Tf, Tc/Tw, CTM via q/Q/cm) to place the
baseline shift.
4. Compose both edits in one BT block, inserting in descending byte-offset order so offsets
stay valid.
5. Keep a raster fallback for fonts it still can't parse — but flag such docs "text-layer lost"
so fail-closed policy can refuse them in invisible mode.

This is the compatibility priority made concrete. Real-world PDFs are a zoo (subsetted fonts,
embedded CMaps, mixed encodings, rotated CTMs, form XObjects). The failure mode isn't "wrong bit"
— it's "corrupted PDF" or "visible artifact," both unacceptable. Confidence here is Medium-High:
the technique is known and pikepdf's TokenFilter is the right tool, but breadth of font/PDF
coverage is where the long tail of real bugs will live. Budget the most time here.

3.2 Channel K's decoder-collapse bug (must fix + recalibrate)

Starfield's ±3 pt symmetric kerning drives "0"-bit gaps toward zero (words touch), which
destroys K's own decoder — the silent column it measures vanishes. Required change: one-sided /
positive-biased
encoding (bit-1 = +ε, bit-0 = 0, never negative) and/or lower ε to ~1.5–2 pt.
Consequence: K's robustness thresholds were calibrated at 150 DPI / ε=3 pt, so the entire K
calibration must be re-run
after the change. This is known, bounded work — but it's real work,
and it gates any Authenticity/Hybrid robustness claim.

3.3 Print-scan survival — the one genuine research risk

The blueprint is honest that print-scan is a design target, not a shipped guarantee. Phone-
camera capture is the proven K case; print→scan adds toner spread, halftoning, scanner MTF, and
rescan skew on top. Whether ε≈1.5–2 pt gap-ratio signal survives that chain is an empirical
question that must be measured
, not reasoned about. This is the single biggest reason overall
confidence is "high" and not "very high." De-risking is §6 DR-2.

3.4 The shared normalization front-end

Every uploaded leak artifact (PDF, screenshot, phone photo, scan, partial crop) routes once
through deskew/deblur/keystone+barrel dewarp, then feeds the rectified image to both decoders.
This is the cheap, high-leverage win — it lifts V's phone-camera survival (V has no dewarp today).
It also forces a DPI re-calibration of V against rectified inputs. CV-standard, but the
calibration coupling means V and K can't be calibrated fully independently; plan a joint multi-DPI
pass.


4. Consistency & correctness — issues to resolve before building

4.1 ⚠️ Contract bug: starmap is attached to the wrong mode

The frozen contract says the vault blob belongs to Traceability, but the design says it belongs
to Authenticity. These directly contradict:

  • proto/stego/v1/stego.proto: bytes starmap = 3; // present for TRACEABILITY/HYBRID only
  • internal/protection/app/ports.go: Starmap []byte // ... populated for Traceability/Hybrid only
  • vs. BUILD_BLUEPRINT.md §6: Channel V = Traceability = blind (ID straight from pixels, no
    vault
    ); Channel K = Authenticity = non-blind (needs the starmap from the vault). §5's proto
    sketch even comments starmap // present only for K/HYBRID — i.e. Authenticity/Hybrid.

Taken literally, the committed contract would have the engine emit a vault blob for the mode that is
supposed to be self-verifying and store nothing for the mode that requires the vault to decode.
Traceability is the public blind tier precisely because it needs no registry; wiring a starmap to it
is backwards. Resolve this in the proto + ports before writing the engine, or the engine and the
seam will disagree on day one. (Low effort to fix — it's a comment/semantics correction and a mode
label — but it must be deliberate, since the contract is "frozen.")

4.2 Payload format is the security keystone — get it right once

Both channels must carry the same in-band payload so one ledger attributes either:
version(4b) | issuance_id | keyed-MAC tag → Reed-Solomon ECC. Two non-negotiables:
- Keyed MAC, not CRC. geomstego's CRC-16 gave false positives and forced a heuristic tiebreaker
— a wrongful-attribution risk. Replace with truncated HMAC(master_key[epoch], issuance_id).
A random decode passes with prob 2⁻ᵀ; size T (24–32 bits) to the legal confidence bar. This is the
false-positive killer and the core of the "security first" priority.
- No PII in-band, ever. issuance_id is an opaque index into the ledger row; the MAC is keyed by
key_epoch for rotation (old epochs stay verifiable). Capacity: V tiles the codeword and
majority-votes across paragraphs; K has more room (RS + 2× block repeat).

4.3 Verdict / tier semantics must match the seam

Decode returns per-channel {issuance_id, mac_ok, confidence} + a Verdict
(CONFIRMED/LIKELY/INCONCLUSIVE) + cross_channel_agreement. Two-tier exposure is a hard
security boundary
: the blind public verify endpoint runs V only (no vault, payload redacted —
"is this traced? y/n"); the forensic console (access-controlled) runs V+K against the vault. K is
registry-bound and can never appear in the public tier.
The Tier enum already encodes this; the
engine must honor it, and the new verify routes must enforce it.


5. Security posture (the top priority) — assessment: strong

  • Keyed-MAC payload eliminates the reference impls' false-positive path — the biggest forensic
    risk in the legacy design. ✅
  • Master secret in KMS/HSM, addressed by key_epoch for rotation; vault blobs encrypted with
    KMS-managed DEKs; mTLS between Go and sidecar; no secrets in source (the reference repos committed
    a stego secret to git — the constraint doc explicitly forbids repeating that). ✅
  • Fail-closed: if watermarking can't complete, the protected download is blocked, never
    served unmarked (configurable per classification). The sidecar being stateless + the Go side owning
    policy makes this clean to enforce. ✅
  • Data-minimization in the vault: store line fingerprints/features for anchoring rather than
    raw line text where robustness allows (GDPR). Watermarks are personal-data processing — DPIA hook,
    retention/erasure path, evidentiary bar before a CONFIRMED verdict can drive personnel action. ✅
    These are designed-in, not bolted-on.
  • Residual security-relevant gaps (must be stated, not hidden): recipient collusion (diffing
    two copies to locate/strip the mark), re-typeset / reflow / OCR-and-reprint (destroys both
    geometric signals), and severe blur. Anti-collusion (Tardos fingerprinting) is a roadmap item, not
    a shipped guarantee. A buyer must be told the threat model: this defeats casual leaking and
    screenshot/photo exfiltration with cryptographic attribution — it is not a defense against a
    determined adversary who retypesets the document.

6. De-risking plan — the order I'd actually build in

The whole strategy is: prove the two scariest claims on a real corpus before committing to the full
build
, because they're the only things that could invalidate the module's core promise.

  • DR-0 (contract): Fix the §4.1 starmap/mode inversion in proto + ports. One PR, do it first.
  • DR-1 (orthogonality spike): Smallest possible V+K encode on a handful of real PDFs; confirm
    cross-talk is near-zero by decoding each channel with the other present. If Hybrid's independence
    doesn't hold empirically, everything downstream changes — so validate it cheaply, first.
  • DR-2 (real-capture corpus): Build the corpus early, not at the end: phone photos at varied
    angles/lighting, messaging-app recompression (WhatsApp/Telegram JPEG), and print→scan at
    multiple DPIs. Measure per-channel trace rate. This is what converts "print-scan is a design
    target" into a published number (or an honest "camera-yes, print-scan-partial" SLA).
  • DR-3 (font-coverage matrix): A test set spanning Type1/TrueType/WinAnsi, Type0/CID, subsetted
    fonts, embedded CMaps, rotated CTMs, form XObjects. Every entry must round-trip to a byte-valid,
    visually-clean
    PDF or fall to the flagged raster path. This is the compatibility gate.
  • DR-4 (K recalibration): Implement the one-sided fix, then re-run K's DPI/ε calibration against
    DR-2's rectified inputs jointly with V.
  • Then build the full sidecar (surgery layer → V → K → normalization → unified verify), wire the
    gRPC adapter behind PROTECTION_ENGINE=grpc, add the two verify routes under
    requireModule("watermarking"), and run the §7 validation/hardening pass (pen-test, DPIA,
    published trace-rate SLA).

The point of this ordering: DR-1 and DR-2 are cheap relative to the full build and can kill or
reshape
the design. Do them before the expensive keystone (§3.1).


7. Confidence, stated plainly

  • Will the module ship and integrate cleanly? Very high. The seam is done, the ledger is live,
    the module-gate exists, the stack is vetted, the references are on disk.
  • Will Traceability (invisible + crop/screenshot + blind verify) hit its promise? High. This is
    the most solid path and I'd expect it to work close to spec.
  • Will Authenticity survive phone-camera capture? Medium-High, pending the K fix + recalibration.
  • Will it survive print→scan at a sellable trace rate? Medium, and unknown until measured — the
    one honest research risk. Sell the SLA on measured numbers, not the design target.
  • Will it resist a determined re-typesetting/colluding adversary? No — and that's a stated,
    bounded limitation, not a surprise.

Bottom line: this is a green-light to build, priorities well-matched (security is the strongest
dimension; compatibility is the hardest but tractable; speed is rightly last). The only true unknown
is print-scan robustness, and the plan front-loads measuring it. Fix the contract inversion, run the
two spikes, then build the keystone.


8. Open decisions for the user (won't block the spikes)

  1. Print-scan scope: ship Authenticity as camera-resilient now, print-scan as a measured/best-
    effort SLA
    , or hold the mode until print-scan clears a bar? (Recommend: ship camera-first, label
    print-scan by the DR-2 number.)
  2. MAC tag width T: 24 vs 32 bits — trades capacity against false-positive floor (2⁻ᵀ). Pick to
    the legal confidence bar for personnel action.
  3. Anti-collusion (Tardos): roadmap-only for v1, or in scope? (Recommend: roadmap; it's a
    separate, large piece and not required for the core leak-attribution promise.)