Documentation
¶
Overview ¶
Package htmlexport renders one run — or one pair of runs — as a single self-contained HTML file.
The terminal is the demo; this is the workhorse. Real runs carry multi-kilobyte payloads that no terminal can show, and the export is what somebody attaches to a ticket, sends to a colleague, or opens six months later in the middle of an incident. It is also the page behalf publishes as its own demo: zero backend, linkable, and literally the product's own output.
Three rules the renderer keeps ¶
**Self-contained, absolutely.** The document makes no network request of any kind: no CDN, no web font, no remote image, no analytics, no fetch. CSS is one inline <style>, script is one inline <script>, every mark is a text glyph or an inline SVG. The page also ships its own restrictive Content-Security-Policy meta tag, so the promise is enforced by the document and not only by the code that wrote it. Assume it will be opened from a file:// URL on a machine with no network, because it will be.
**Absence renders.** Payloads are customer-held (Q34, D7), so a run exported on a machine whose store was pruned is mostly placeholders — and it is still evidence, because the receipts carry the digests regardless (Q83). Every slot renders: content when it is present and hashes to its commitment, a typed placeholder when it does not, and a visible tamper finding — committed digest, actual digest, changed field paths — when the stored bytes contradict the signed receipt. A page full of placeholders must read as evidence, never as a broken document.
**Nothing is claimed that the bytes do not support.** Every hop states what was checked and what was not; the suppression rule is labelled a heuristic exactly as the terminal labels it; names come from the local alias map and say so; and the page carries the README's threat model, so a reader who has never met behalf cannot come away over-trusting the document. The page also names itself a rendering and points at the bytes that are the evidence, with the exact offline command that re-checks them.
What it is not ¶
It is not the terminal's ANSI layout transcribed into HTML. Different medium, different design: reading-width prose, real hierarchy, monospace reserved for the things where it means something (digests, identifiers, payload bytes), and a print stylesheet, because these get attached to tickets as PDFs.
Index ¶
- Constants
- func Render(w io.Writer, p *Page) error
- type Claim
- type Command
- type DiffBlock
- type DiffRow
- type DiffView
- type DiffWarning
- type HopView
- type LogIdentity
- type Options
- type Page
- type ReceiptView
- type ReorderView
- type Rollup
- type RollupRow
- type RunView
- type SlotView
- type StateNote
- type TrustBlock
Constants ¶
const DefaultCollapseBytes = 600
DefaultCollapseBytes is the rendered-payload size past which a slot's content starts collapsed behind its disclosure control. Roughly a dozen lines of pretty-printed JSON: enough that a small argument object reads inline, little enough that a 40 KB tool response never pushes the next receipt off the page.
const DefaultMaxInlineBytes = 256 << 10
DefaultMaxInlineBytes bounds how much of one payload is written into the document. Collapsing keeps a 40 KB blob usable; a 20 MB blob is a different problem, and inlining it would produce a file nobody can open. Past this bound the head of the content is shown with an explicit, visible note naming how many bytes were left out — the digest still commits to all of them, and the note says where to get the rest.
Variables ¶
This section is empty.
Functions ¶
func Render ¶
Render writes the page.
The document is assembled by html/template, which escapes in context. That matters more here than in most renderers: everything on this page came out of a receipt, and a receipt records what an agent said. Operation names, targets, labels and payload bytes are all attacker-influenceable in the threat model this product is about (Q74), so none of them may reach the output unescaped, and none of them do — the template has no raw-HTML interpolation anywhere. The only pre-typed values in the whole page are the stylesheet, the script and the rollup bar's widths, all three of them constants or computed numbers, none of them derived from a receipt.
Types ¶
type Claim ¶
Claim is one line of the trust block: a short label and the sentence that qualifies it.
type DiffBlock ¶
type DiffBlock struct {
// Label is the step coordinate, showing both ordinals when alignment put
// different ones opposite each other.
Label string
// RunA and RunB name the two runs, so the side-by-side columns are
// headed by the thing they are rather than by "A" and "B".
RunA string
RunB string
AnchorA string
AnchorB string
StepA int // -1 when the pair has no counterpart on that side
StepB int
Operation string
Target string
Actor string
Classes []string
// Coords is where in the chain and how far into the run — "hop 3, t+60s".
Coords string
// Rows are the field-level findings, one per differing path.
Rows []DiffRow
// Reordered is the same-elements-different-sequence finding, which is
// the divergence that reads as "nothing changed" to every other tool.
Reordered *ReorderView
// Missing names the run that has no counterpart step, for an insertion
// or a deletion.
MissingFrom string
// Truncated is set when the pair carried more field-level changes than
// the engine keeps.
Truncated bool
NoiseFiltered []string
// Opaque marks a digest-only difference.
Opaque bool
// Suppressed marks a difference the downstream heuristic hid from the
// causal view.
Suppressed bool
}
DiffBlock is one aligned pair that is not identical.
type DiffRow ¶
type DiffRow struct {
Path string
A string
B string
// Kind is changed | only-in-A | only-in-B.
Kind string
Class string
// Gloss is a display convention the renderer added, never something the
// bytes say — currently only the minor-units reading of a `_cents`
// field. Stated separately so it can never be mistaken for stored data.
GlossA string
GlossB string
}
DiffRow is one differing path, with both stored values.
type DiffView ¶
type DiffView struct {
RunA, RunB string
CountA, CountB int
// Summary is the headline sentence.
Summary string
// Aligner names the tier that produced the pairing (Q85), so the reader
// knows whether steps were matched by stored key or by sequence.
Aligner string
AlignerNote string
First *DiffBlock
Featured *DiffBlock
// FeaturedIsFirst is true when the engine featured the first divergence
// itself — there is nothing after it to show. Templates cannot compare
// pointers, and this is the comparison they need.
FeaturedIsFirst bool
// FeaturedIsConsequence distinguishes the strong claim ("this value came
// out of that step and went into this one") from the weak one ("a later
// difference").
FeaturedIsConsequence bool
FeaturedTitle string
LinkText string
// All is every difference in aligned order, including the suppressed
// ones. The default terminal view hides them behind a count; the page
// has room to show them, and the suppression note still names the rule.
All []DiffBlock
// Opaque are the pairs whose only difference is a payload-slot digest:
// the receipt records that customer-held content changed, not what
// changed in it. Never named as a cause.
Opaque []DiffBlock
SuppressedCount int
SuppressionNote string
OpaqueNote string
// Warnings are the handoffs to `behalf why`: the featured step sits on a
// receipt whose stored attribution is not `verified`.
Warnings []DiffWarning
// AttributionNote states a run-wide authority difference that the action
// diff deliberately does not count.
AttributionNote string
WeakestA, WeakestB string
}
DiffView is the comparison, led by the answer: which step diverged first, and the one later step the divergence can be SHOWN to have reached.
The claims here are exactly the engine's claims, at exactly the engine's strength (internal/diff, causality.go): the first divergence is a fact about the alignment; the consequence is exhibited by value equality or it is not called a consequence; and the downstream suppression is a heuristic, labelled as one on the page, with every suppressed difference still listed further down. A rendering that quietly hardened any of those would be worse than no rendering.
type DiffWarning ¶
type DiffWarning struct {
Operation string
RunID string
Actor string
State string
Command string
// Unattributed is set when the receipt carries no delegation chain at
// all, which is a different sentence from a weak one.
Unattributed bool
}
DiffWarning is one attribution handoff.
type HopView ¶
type HopView struct {
Depth int
MaxDepth int
Label string
JKT string
// Status is the stored per-hop three-state: verified, asserted, broken
// (Q12, D5).
Status string
// StatusWord is the display form; asserted is named, never collapsed
// into failure.
StatusWord string
Method string
// Evidence is the human-readable evidence column: what makes this hop
// verified, or empty when nothing does.
Evidence string
EvidenceRef string
// Checked and NotChecked are the per-state statement of what was and
// was not established. Every `asserted` hop carries them; so does every
// other state, because an unqualified "verified" is the more dangerous
// half.
Checked []string
NotChecked []string
Intent string
Scope string
// Attenuation is the read-time comparison against the parent hop,
// stamped with the comparator version (Q11, Q13). Only shown when it
// says something the reader must not miss.
Attenuation string
AttenuationReason string
Credential why.Credential
RootBinding *why.RootBinding
Carriage string
JTI string
ParHash string
Exp string
}
HopView is one delegation hop, with the honesty furniture attached: what this hop's state actually rests on, and what it does not.
type LogIdentity ¶
type LogIdentity struct {
Dir string
Origin string
TreeSize uint64
RootHex string
// Checkpoint is the signed note verbatim, as written in the log dir.
Checkpoint string
// Available is false when the checkpoint could not be read or did not
// verify. The page then says so rather than showing a blank identity.
Available bool
// Commands are the verification commands, in the order to run them.
Commands []Command
}
LogIdentity is the page's answer to "what bytes is this a rendering of, and how do I check them myself". It is the export's chain head: the signed checkpoint's origin, size and root hash, plus the exact command a sceptic runs offline (Q29, Q18).
type Options ¶
type Options struct {
// LogDir is the tlog-tiles directory to read. Required.
LogDir string
// Runs is one or two run ids. One renders the single-run page; two
// render the diff-led comparison.
Runs []string
// Store is the customer's payload CAS. A nil store is legal and
// common: every slot then resolves `missing` and renders as a
// placeholder, which is the normal path (Q83).
Store *cas.Store
// Aliases turns key thumbprints into display labels (Q16). Labels are
// asserted, never evidence, and the page says so.
Aliases why.Aliases
// Now stamps the document's generation time. Zero means time.Now.
Now time.Time
// CollapseBytes and MaxInlineBytes override the defaults above.
CollapseBytes int
MaxInlineBytes int
}
Options configures one export.
type Page ¶
type Page struct {
// Title is the document title and the <h1>.
Title string
// Subtitle names what the page is: one run, or two runs compared.
Subtitle string
// GeneratedAt is when this rendering was produced, RFC 3339 UTC. It is
// a property of the RENDERING, never of the evidence: the receipts
// carry their own capture times and this does not touch them.
GeneratedAt string
// Pair is true when two runs were given and the page leads with a diff.
Pair bool
Log LogIdentity
Diff *DiffView
Runs []*RunView
Trust TrustBlock
// Notes collect anything this rendering could not show honestly from
// the stored data — a missing checkpoint, an unreadable alias map. They
// are printed on the page, not swallowed.
Notes []string
// Findings counts the payload slots whose stored bytes contradict the
// digest committed in their signed receipt, across every run on the
// page. The caller's warning line hangs off this.
Findings int
}
Page is one exported document.
func Build ¶
Build assembles the page model. It reads: the index for the run views (log-index order filtered to the run — the authoritative reconstruction order, Q82), the log's own entry bundles for the receipt bytes, and the customer's CAS for the payloads. Every payload is checked against its indexed leaf hash before anything is read out of it, so nothing on the page comes from bytes the index does not vouch for.
Nothing is written. This is a read path over the log dir: no appender is started and no epoch is claimed, so exporting never fences a running log service (Q57).
type ReceiptView ¶
type ReceiptView struct {
Step int
Anchor string
LogIndex uint64
LeafHash string
ReceiptID string
Kind string
RunID string
CapturedAt string
// Elapsed is the offset from the run's first capture — "t+60s".
Elapsed string
Operation string
Target string
Outcome string
OutcomeOK bool
Amount string
Currency string
Actor string
ActorJKT string
// Attribution and Class are the stored rollup and the stored attribution
// class (Q12, §8), read and never recomputed.
Attribution string
Class string
Hops []HopView
VerifiedHops int
TotalHops int
// Excess is the read-time scope finding: the operation exceeded the
// ceiling the chain delegated. Recorded, never enforced (Q11, Q45).
Excess *why.ScopeExcess
Slots []SlotView
// Findings is how many of this receipt's slots are tamper findings.
Findings int
// Differs marks a receipt the diff named as differing from its
// counterpart, so the timeline can point at it.
Differs bool
}
ReceiptView is one receipt: what happened, what authorised it, and what the payload slots resolved to.
type ReorderView ¶
type ReorderView struct {
Path string
Count int
Index int
// Fields are the differing sub-fields, named once because both sides
// show the same fields in the same order.
Fields []string
RowsA []string
RowsB []string
// GlossA and GlossB parallel Rows with the renderer's display
// conventions, empty where none applies.
GlossA []string
GlossB []string
}
ReorderView is the positional reading of a reordered array: the first position at which the two runs hold different elements, and the sub-fields of that element that actually differ.
type Rollup ¶
type Rollup struct {
Denominator int
Rows []RollupRow
// Note states what the denominator is and where the numerators come
// from — the reproducibility half of Q86.
Note string
}
Rollup is the unattributed-rate metric for one run (Q86): numerators per verification state over a stated denominator, so a reader can reproduce the number from their own receipts.
type RollupRow ¶
type RollupRow struct {
State string
Count int
Percent string
// Width is the bar segment's width. It is a CSS length by type, so the
// template writes it into a style attribute without the escaper having
// to guess whether a computed string is safe there.
Width template.CSS
}
RollupRow is one state's share.
type RunView ¶
type RunView struct {
ID string
Started string
Ended string
// Status is `ok` unless some receipt records a failed operation. It is
// not a completeness claim: run completeness is marked by a session-end
// receipt, and the frozen kind enum has no such kind yet (Q82).
Status string
// Receipts is every receipt in the run view — log-index order filtered
// to the run, the authoritative reconstruction order (Q58, Q82).
Receipts []*ReceiptView
// Actions counts the action-family receipts: the denominator of the
// attribution metric (Q6, Q86).
Actions int
// Actor names the human at the root of the delegation chain — who the
// run was carried out on behalf of. A display label off the local alias
// map (Q16): asserted, never evidence.
Actor string
ActorJKT string
// Attribution is the run's weakest stored rollup (Q12).
Attribution string
// Rollup is the Q86 metric: the share of action receipts at each
// verification state, with its denominator stated.
Rollup Rollup
// PayloadSummary counts the resolved slot states across the run.
PayloadSummary string
// Findings is how many slots contradicted their commitment.
Findings int
// Anchor is the run section's html id.
Anchor string
}
RunView is one run's header, timeline and receipts.
type SlotView ¶
type SlotView struct {
Label string
Role string
State string
Committed string
Custody string
ContentType string
Size int
SizeText string
Digest string
Ref string
CauseRef string
Subjects []string
// Placeholder is the typed stand-in for a non-present slot, rendered by
// internal/payload so the HTML and the NDJSON say the same thing.
Placeholder string
// Content is the rendered payload: pretty-printed when the bytes are
// JSON, verbatim when they are text, a typed summary when they are
// binary. Non-empty only for a present slot.
Content string
// Language hints the content kind for display: "json", "text" or
// "binary".
Language string
// Collapsed is true when the content starts behind its disclosure
// control because it is long.
Collapsed bool
// Truncated is set when the content exceeded MaxInlineBytes; Omitted is
// how many bytes were left out.
Truncated bool
Omitted int
// Tampered marks the payload cover-up: bytes in the store that do not
// hash to the digest the signed receipt commits to.
Tampered bool
Mismatch *payload.Mismatch
// Err records a lookup that failed for a reason that is neither absence
// nor a mismatch — a bad mount, a permission denial. Such a slot is
// `unreadable` too, and is deliberately NOT a tamper finding.
Err string
// ManifestFields is how many field digests the receipt committed for
// this slot (Q37); zero means whole-blob only, which is a gap in the
// evidence rather than a clean bill.
ManifestFields int
}
SlotView is one payload slot, joined against the customer's store.
type TrustBlock ¶
TrustBlock is the honesty furniture: what a reader may conclude from this document, and what they may not. The wording is the README's threat model (Q74, Q29), reused rather than reinvented, because the limits are the product's own published claims.