htmlexport

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

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

View Source
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.

View Source
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

func Render(w io.Writer, p *Page) error

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

type Claim struct {
	Label string
	Body  string
}

Claim is one line of the trust block: a short label and the sentence that qualifies it.

type Command

type Command struct {
	Line string
	What string
}

Command is one shell line the page shows, with what it establishes.

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

func Build(ctx context.Context, opt Options) (*Page, error)

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).

func Write

func Write(ctx context.Context, w io.Writer, opt Options) (*Page, error)

Write builds the page for opt and renders it to w.

func WriteFile

func WriteFile(ctx context.Context, path string, opt Options) (*Page, error)

WriteFile renders the page to path, writing through a temporary file in the same directory so an interrupted export never leaves a half-written document that looks like a complete one.

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 StateNote

type StateNote struct {
	State string
	Body  string
}

StateNote explains one of the three verification states.

type TrustBlock

type TrustBlock struct {
	Proves    []Claim
	NotProves []Claim
	States    []StateNote
	Footnote  string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL