Liking cljdoc? Tell your friends :D

hive-system.redaction.core

Redaction registry + walker for safely surfacing structured data (compiled plans, event payloads, env maps, log lines) to an AI assistant or any debug surface.

Model

A rule is a map:

{:select <selector> ; path-vector OR predicate fn (key,val)→bool :replace <fn> ; (fn [val] -> replacement) :id <keyword> ; assigned by register-rule! :doc <string?>}

Rules are stored in a global atom registry. redact walks input via clojure.walk/postwalk, applying matching rules and auto-replacing any Tainted it encounters with its print-method form.

Selector forms

  • Vector path — [:env "SSHPASS"] matches the value at that path inside any nested map.
  • Predicate fn — (fn [k v] ...) invoked for every map entry; truthy return → replace the value.

Default rules

Registered at namespace load time:

:env/sshpass → [:env "SSHPASS"] → "<redacted>" :env/ssh-auth-sock → [:env "SSH_AUTH_SOCK"] → "<redacted>" :env/ssh-askpass → [:env "SSH_ASKPASS"] → "<redacted>" :env/ssh-private-key → [:env "SSH_PRIVATE_KEY"] → "<redacted>"

Tainted handling

Any Tainted encountered during the walk is replaced with its pr-str form (the redacted print-method output) — no rule needed.

Privacy contract

redact never invokes untaint and never reads :value directly. It is safe to compose into a logger appender.

Redaction registry + walker for safely surfacing structured data
(compiled plans, event payloads, env maps, log lines) to an AI
assistant or any debug surface.

## Model

A *rule* is a map:

  {:select  <selector>     ; path-vector OR predicate fn (key,val)→bool
   :replace <fn>           ; (fn [val] -> replacement)
   :id      <keyword>      ; assigned by register-rule!
   :doc     <string?>}

Rules are stored in a global atom registry. `redact` walks input via
`clojure.walk/postwalk`, applying matching rules and auto-replacing
any `Tainted` it encounters with its print-method form.

## Selector forms

- Vector path  — `[:env "SSHPASS"]` matches the value at that path
                 inside any nested map.
- Predicate fn — `(fn [k v] ...)` invoked for every map entry; truthy
                 return → replace the value.

## Default rules

Registered at namespace load time:

  :env/sshpass         → [:env "SSHPASS"]        → "<redacted>"
  :env/ssh-auth-sock   → [:env "SSH_AUTH_SOCK"]  → "<redacted>"
  :env/ssh-askpass     → [:env "SSH_ASKPASS"]    → "<redacted>"
  :env/ssh-private-key → [:env "SSH_PRIVATE_KEY"] → "<redacted>"

## Tainted handling

Any `Tainted` encountered during the walk is replaced with its
`pr-str` form (the redacted print-method output) — no rule needed.

## Privacy contract

`redact` never invokes `untaint` and never reads `:value` directly.
It is safe to compose into a logger appender.
raw docstring

hive-system.redaction.ssh-argv

Structural redactor for ssh argv vectors.

Goal

Preserve the shape of the argv (every flag in its place, every option preserved, count unchanged) while replacing Tainted slots with stable hash tokens like <host:#a3f4> / <id:#7c2>.

The redactor is structurally aware: it understands the standard ssh(1) flag syntax and knows which slots can hold a hostname / identity / port. But it is intentionally CONSERVATIVE — it ONLY touches slots whose contents are Tainted. Plain strings pass through unchanged.

Why conservative

A hostname is whatever the user happens to call their VPS. We don't want to pattern-match hostnames (false positives, false negatives). Instead, the credential resolver / plan compiler decides what is sensitive by wrapping in Tainted. The redactor's job is to take that signal and present it as a token rather than a <redacted ...> blob — preserving structural fidelity for debugging.

Recognized ssh flag idioms

Single-arg flags (the next argv slot is the value):

-l <user> → identity -i <keyfile> → identity (keyfile path) -p <port> → port -o <key=val> → option (passed through; we don't parse it) -F, -E, -S, -W, -L, -R, -D, -J, -Q, -e, -m, -c, -O, -B (each of the above takes a value in the following slot)

Boolean flags (no following value): -1 -2 -4 -6 -A -a -C -f -G -g -K -k -M -N -n -q -s -T -t -V -v -X -x -Y -y

Anything that doesn't look like a flag and appears after we've consumed flags is treated as the destination (host, or user@host).

Behavior

Plain strings: pass through verbatim. Tainted slots: replaced with their <src:#hash> token form. If a Tainted carries :source :argv-host, you get <host:#xxxx>; for :argv-id you get <id:#xxxx>; the source label IS the token prefix.

Repeated Tainted with the same underlying value within a single argv produces the SAME token (because the hash is value-stable within a JVM). Operators can spot "this argv references the same host twice" at a glance.

The function does NOT call untaint and does NOT lengthen or shorten the argv. Output count == input count.

Structural redactor for ssh argv vectors.

## Goal

Preserve the *shape* of the argv (every flag in its place, every
option preserved, count unchanged) while replacing Tainted slots with
stable hash tokens like `<host:#a3f4>` / `<id:#7c2>`.

The redactor is structurally aware: it understands the standard
ssh(1) flag syntax and knows which slots can hold a hostname /
identity / port. But it is intentionally CONSERVATIVE — it ONLY
touches slots whose contents are `Tainted`. Plain strings pass
through unchanged.

## Why conservative

A hostname is whatever the user happens to call their VPS. We don't
want to pattern-match hostnames (false positives, false negatives).
Instead, the credential resolver / plan compiler decides what is
sensitive by wrapping in `Tainted`. The redactor's job is to take
that signal and present it as a token rather than a `<redacted ...>`
blob — preserving structural fidelity for debugging.

## Recognized ssh flag idioms

Single-arg flags (the next argv slot is the value):

  -l <user>          → identity
  -i <keyfile>       → identity (keyfile path)
  -p <port>          → port
  -o <key=val>       → option (passed through; we don't parse it)
  -F, -E, -S, -W, -L, -R, -D, -J, -Q, -e, -m, -c, -O, -B
     (each of the above takes a value in the following slot)

Boolean flags (no following value): -1 -2 -4 -6 -A -a -C -f -G -g
  -K -k -M -N -n -q -s -T -t -V -v -X -x -Y -y

Anything that doesn't look like a flag and appears after we've
consumed flags is treated as the destination (host, or user@host).

## Behavior

Plain strings: pass through verbatim.
Tainted slots: replaced with their `<src:#hash>` token form. If a
               Tainted carries `:source` :argv-host, you get
               `<host:#xxxx>`; for :argv-id you get `<id:#xxxx>`;
               the source label IS the token prefix.

Repeated Tainted with the same underlying value within a single
argv produces the SAME token (because the hash is value-stable
within a JVM). Operators can spot "this argv references the same
host twice" at a glance.

The function does NOT call untaint and does NOT lengthen or shorten
the argv. Output count == input count.
raw docstring

hive-system.redaction.tainted

Tainted-value wrapper for sensitive material that must NOT reach an AI assistant or any debug surface — but whose presence and identity should still be observable via stable per-run hash tokens.

Purpose

Where hive-system.secrets.core/Secret is the storage primitive for credentials ("don't print, don't compare, don't serialize"), Tainted is a diagnostic primitive: it wraps any value (hostnames, ports, identities, env values, argv slots) so we can correlate the same underlying value across many log lines with a short hash token like <host:#a3f4> — without ever printing the value itself.

Type

Tainted is a defrecord with three fields:

:value — the wrapped sensitive value :source — non-sensitive label (e.g. "pass:vps/r1/ip", :argv-host) :hash — 4-hex-char stable token derived from HMAC-SHA256(salt, value)

Hash semantics

  • Hash is computed once at construction and cached.
  • Same value within a single JVM run produces the SAME hash token, so operators can correlate <host:#a3f4> across log lines.
  • Salt is generated per JVM at namespace-load time via SecureRandom and held in a private atom — cross-JVM-restart unlinkability is by design (the salt cannot be reconstructed from prior runs).
  • Hash space is 16^4 = 65,536 — collision probability is acceptable within a single run's working set of distinct sensitive values.

Print safety

print-method ALWAYS emits <redacted src=... h=#xxxx> and NEVER the underlying value. toString mirrors that.

Audit surface

  • untaint — the ONE blessed unwrap. Every call site is a leak risk. This is the only safe extraction point and MUST only be invoked at trust boundaries (process spawn, network write, file write to a trusted local sink). Never call it within reach of a log appender, event subscriber, or anything whose output may be surfaced to the assistant.

Non-goals

  • Encryption / secure memory wiping. JVM strings can't be reliably zeroed; the same caveats as Secret apply.
  • Cross-process correlation. By design — see Hash semantics above.
Tainted-value wrapper for sensitive material that must NOT reach an AI
assistant or any debug surface — but whose *presence and identity*
should still be observable via stable per-run hash tokens.

## Purpose

Where `hive-system.secrets.core/Secret` is the storage primitive for
credentials ("don't print, don't compare, don't serialize"), `Tainted`
is a *diagnostic* primitive: it wraps any value (hostnames, ports,
identities, env values, argv slots) so we can correlate the same
underlying value across many log lines with a short hash token like
`<host:#a3f4>` — without ever printing the value itself.

## Type

`Tainted` is a defrecord with three fields:

  :value  — the wrapped sensitive value
  :source — non-sensitive label (e.g. "pass:vps/r1/ip", :argv-host)
  :hash   — 4-hex-char stable token derived from HMAC-SHA256(salt, value)

## Hash semantics

- Hash is computed once at construction and cached.
- Same value within a single JVM run produces the SAME hash token,
  so operators can correlate `<host:#a3f4>` across log lines.
- Salt is generated per JVM at namespace-load time via SecureRandom
  and held in a private atom — cross-JVM-restart unlinkability is by
  design (the salt cannot be reconstructed from prior runs).
- Hash space is 16^4 = 65,536 — collision probability is acceptable
  within a single run's working set of distinct sensitive values.

## Print safety

`print-method` ALWAYS emits `<redacted src=... h=#xxxx>` and NEVER
the underlying value. `toString` mirrors that.

## Audit surface

- `untaint` — the ONE blessed unwrap. Every call site is a leak risk.
  This is the only safe extraction point and MUST only be invoked at
  trust boundaries (process spawn, network write, file write to a
  trusted local sink). Never call it within reach of a log appender,
  event subscriber, or anything whose output may be surfaced to the
  assistant.

## Non-goals

- Encryption / secure memory wiping. JVM strings can't be reliably
  zeroed; the same caveats as `Secret` apply.
- Cross-process correlation. By design — see Hash semantics above.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close