tlog

package
v0.1.2 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: 36 Imported by: 0

Documentation

Overview

Package tlog is the behalf log service: a Tessera tiled transparency log on the POSIX driver (architecture D1), one appender per log dir (Q57), with receipt-id dedup in front of the log (Q46) and an SCT-style receipt promise returned with every ack (D2).

Durability contract (Q75, verified from Tessera source at v1.0.4): the POSIX driver resolves the Add future only after the entry bundle, tiles and tree state are durably written (O_SYNC temp files, atomic renames, directory fsyncs) — so a resolved ack means the entry is durably committed AND integrated into the on-disk tiles. The signed checkpoint covering it publishes asynchronously (1 s interval). The promise returned with the ack is not an inclusion proof — it is redeemable at checkpoint publication.

Backpressure (Q47): WithPushback is a silent no-op on the POSIX driver and integration runs inline, so overload surfaces as Append latency growth, not a pushback error. No pushback option is configured here.

Index

Constants

View Source
const (
	DefaultCheckpointInterval = 1 * time.Second
	DefaultBatchMaxAge        = 250 * time.Millisecond
	DefaultBatchMaxSize       = 256
)

Defaults per architecture Q30/D3.3.

View Source
const (
	// DefaultWitnessFailOpen is Q96's v1 policy.
	DefaultWitnessFailOpen = true
	// DefaultWitnessTimeout bounds one witnessing pass. Chosen against the
	// 1 s checkpoint cadence (D3.3) and the 10 s MMD (Q57): a witness pass
	// must finish well inside the interval that produced it, or passes pile
	// up. Note that Tessera's own DefaultWitnessTimeout is 5 s at v1.0.4
	// (D3.5 records 1 s; the source disagrees) — behalf sets its own rather
	// than inheriting either.
	DefaultWitnessTimeout = 1 * time.Second
	// WitnessConfigFileName is the witness policy inside the log dir.
	WitnessConfigFileName = "witnesses.json"
	// WitnessDirName holds the per-checkpoint witness records.
	WitnessDirName = "witness"
	// WitnessOutcomesFileName is the append-only per-checkpoint record.
	WitnessOutcomesFileName = "outcomes.jsonl"
	// WitnessedCheckpointFileName is the latest published checkpoint with
	// the cosignatures behalf holds appended to it, so an export can carry
	// the cosignature alongside the checkpoint. Verifiers that do not know
	// the witness key skip the extra line (D3.4's grease discipline).
	WitnessedCheckpointFileName = "checkpoint.witnessed"
)

Witness policy defaults. Both are explicit configuration; these are the documented values used when the config says nothing.

View Source
const EmittersFileName = "emitters.jsonl"

EmittersFileName holds the public JWKs of every emitter key registered with this log, one JSON object per line, under keys/.

The one thing that was not rebuildable

Q76's claim is that the follower index is disposable: delete it, replay the entry bundles, get it back. The keys table was the single exception, and it was not a soft one. Stored envelopes carry key *thumbprints* only, so a replay recovers no JWK, and the export bridge needs the JWK to write a header — `behalf-log export` after a reindex failed outright with "exportv1: header requires at least one key". A log you could not export from until you happened to re-ingest was a log whose evidence was hostage to a cache.

So registration writes here as well as into the index, and Open replays this file back. The file lives under `keys/`, which every serving configuration already excludes (see the note above), so this publishes nothing that was not already public — these are public keys — while keeping them out of the served tile directory where a reader might mistake them for log content.

What this is NOT

It is not a trust anchor and it must not be read as one. A file on the log operator's own disk, editable by the log operator, cannot establish that an emitter key belongs to anyone: swap a line here and every receipt signed by the new key still verifies against it. That is exactly the gap the published key log (ENG-31) exists to close, and the honest scope of this file is "recover what the index knew", not "say whose key this is".

Append-only and idempotent: re-registering a jkt appends nothing new, so the file does not grow with process restarts.

View Source
const EnvelopeVersion = envelope.Version

EnvelopeVersion is the version string stamped on every stored envelope.

View Source
const IndexFileName = index.FileName

IndexFileName is the SQLite index file inside the log dir: the follower index (internal/index) — a derived, rebuildable projection of the log (Q55, Q56, Q76) whose persistent receipt_id window is also the ingest dedup window (Q46).

View Source
const PayloadTypePromise = "application/vnd.behalf.promise+json"

PayloadTypePromise is the DSSE payloadType for receipt promises.

View Source
const PromiseMMDSeconds = 10

PromiseMMDSeconds is the maximum merge delay carried in every promise: the window within which the promised entry must be covered by a published checkpoint (architecture Q57: checkpoint cadence plus witness timeouts).

View Source
const PromiseVersion = "behalf.sh/promise/v1"

PromiseVersion is the promise statement version string.

Variables

View Source
var ErrFenced = errors.New("tlog: fenced: a newer epoch exists for this log dir")

ErrFenced is returned when this log handle's epoch is no longer the newest recorded in the epoch file: a newer claimant exists and this handle must stop appending and signing promises.

Functions

func BuildEnvelope

func BuildEnvelope(payloadType string, payload []byte, keyid string, sig []byte) []byte

BuildEnvelope assembles the stored log-entry bytes: the DSSE-signed receipt envelope, with the payload spliced verbatim (the span rule, docs/export-format-v1.md §1.2 — the signed bytes are the stored bytes). The log's Merkle leaf covers these exact envelope bytes (receipt-schema-v1.md §2).

func ConsistencyProof

func ConsistencyProof(ctx context.Context, dir string, from, to uint64) ([][]byte, error)

ConsistencyProof returns the RFC 6962 consistency proof between two tree sizes of the log in dir, read from its stored hash tiles.

This is what a witness needs in order to accept a larger tree without trusting the log: the proof carries the root the witness already holds forward to the root the new checkpoint declares (architecture Q29/Q76).

`to` must not exceed the size the published checkpoint commits to — the tiles above it may be partially written.

func ExportRun

func ExportRun(ctx context.Context, dir, runID string, w io.Writer, opts ...ExportOption) error

func InclusionProof

func InclusionProof(ctx context.Context, dir string, index, size uint64) ([][]byte, error)

InclusionProof returns the RFC 6962 inclusion proof for one leaf index in a tree of the given size, read from the log dir's hash tiles.

func LoadEmitterKeys

func LoadEmitterKeys(dir string) (map[string]string, error)

LoadEmitterKeys reads the registered emitter keys as jkt -> JWK JSON. A missing file is not an error: a log that has never had a key registered has none, which is a fact rather than a fault.

func LoadVerifierKey

func LoadVerifierKey(dir string) (string, error)

LoadVerifierKey reads the public verifier key file under dir/keys.

func Reindex

func Reindex(ctx context.Context, dir string) (*index.Stats, error)

Reindex rebuilds the follower index from the entry bundles and restores the registered emitter keys on top of it.

The second half is what makes Q76's "the index is disposable" true rather than nearly true. index.Rebuild replays the log, and the log carries key *thumbprints* only — so a bare rebuild produced an index that knew every receipt and no key, and `behalf-log export` then failed outright with "header requires at least one key". A log you could not export from until something happened to re-register a key is a log whose evidence was hostage to a cache.

This lives here rather than in internal/index because the keys file is the log service's layout, and the index has no business knowing where the log keeps its keys — it is a projection, and a projection that reached back into the thing it projects would stop being rebuildable in a different way.

func SaveCheckpointKey

func SaveCheckpointKey(dir string, k *CheckpointKey) error

SaveCheckpointKey writes the key files under dir/keys.

func SaveWitnessPolicy

func SaveWitnessPolicy(dir string, p *WitnessPolicy) error

SaveWitnessPolicy writes <dir>/witnesses.json.

func VerifyConsistency

func VerifyConsistency(fromSize, toSize uint64, pf [][]byte, fromRoot, toRoot []byte) error

VerifyConsistency checks a consistency proof between two tree heads. It is the read-side counterpart of ConsistencyProof, offered here so callers do not have to reach for the merkle package (and its hasher) themselves.

func VerifyInclusion

func VerifyInclusion(index, size uint64, leafHash []byte, pf [][]byte, root []byte) error

VerifyInclusion checks an inclusion proof for leafHash at index in a tree of the given size and root.

func WitnessConfigPath

func WitnessConfigPath(dir string) string

WitnessConfigPath is where LoadWitnessPolicy looks.

func WitnessOutcomesPath

func WitnessOutcomesPath(dir string) string

WitnessOutcomesPath is the per-checkpoint record file for a log dir.

func WitnessedCheckpointPath

func WitnessedCheckpointPath(dir string) string

WitnessedCheckpointPath is the cosigned checkpoint file for a log dir.

Types

type AppendResult

type AppendResult struct {
	// Index is the leaf index durably assigned to this receipt's envelope
	// (the original index if Duplicate).
	Index uint64
	// Duplicate is true when the receipt_id was already in the log; the
	// envelope was NOT appended again and Index/LeafHash are the original
	// entry's (Q46: duplicates are legal-but-flagged, never appended twice).
	Duplicate bool
	// LeafHash is the RFC 6962 leaf hash of the stored envelope bytes.
	LeafHash [32]byte
	// Promise is the signed receipt promise (the CT SCT analogue). It is
	// returned synchronously with the ack and is not an inclusion proof:
	// it is redeemable against a checkpoint published within mmd_s seconds.
	Promise *SignedPromise
}

AppendResult is the ack for one envelope.

type BundleReader

type BundleReader struct {
	// contains filtered or unexported fields
}

BundleReader is a read-only view over one log dir's entry bundles: it serves the stored envelope bytes (and their payload spans) by log index, caching each bundle it touches. No appender is started and no epoch is claimed, so a reader never fences the running log service (Q57) — this is the read path `behalf why` and `behalf runs` sit on, alongside ExportRun and index.Reconstruct.

The published checkpoint bounds what is readable: an index at or beyond the signed tree size is refused rather than served from a partially written tile, so nothing is ever rendered that a checkpoint does not commit to.

func NewBundleReader

func NewBundleReader(ctx context.Context, dir string) (*BundleReader, error)

NewBundleReader opens dir for reading and parses (signature-verifying) its published checkpoint.

func (*BundleReader) Checkpoint

func (r *BundleReader) Checkpoint() *LogCheckpoint

Checkpoint returns the published checkpoint this reader is bounded by.

func (*BundleReader) Envelope

func (r *BundleReader) Envelope(ctx context.Context, logIndex uint64) ([]byte, error)

Envelope returns the stored envelope bytes at logIndex — the exact bytes the Merkle leaf covers.

func (*BundleReader) Payload

func (r *BundleReader) Payload(ctx context.Context, logIndex uint64, wantLeafHash string) ([]byte, error)

Payload returns the exact stored payload span at logIndex — the signed bytes, spliced out with a span scanner and never re-serialized (the span rule). When wantLeafHash is non-empty (hex, as the index stores it) the envelope is re-hashed and checked against it first, so a caller never reads bytes the index does not vouch for.

type CheckpointKey

type CheckpointKey struct {
	Origin  string // the note key name == checkpoint origin line
	SKey    string // note-format private key ("PRIVATE+KEY+...")
	VKey    string // note-format verifier key
	Private ed25519.PrivateKey
	Public  ed25519.PublicKey
	JWK     dsse.JWK
	JKT     string // RFC 7638 thumbprint of JWK
}

CheckpointKey is the log's Ed25519 checkpoint key in both forms behalf needs: the note-format signer/verifier strings Tessera expects (WithCheckpointSigner takes a note.Signer, and the signer's name becomes the checkpoint origin line), and the raw Ed25519 key pair used to sign receipt promises (architecture D2/Q57: only the current lock-holder's checkpoint key signs promises).

func GenerateCheckpointKey

func GenerateCheckpointKey(origin string) (*CheckpointKey, error)

GenerateCheckpointKey creates a fresh Ed25519 checkpoint key whose note name (and therefore checkpoint origin) is origin.

func LoadCheckpointKey

func LoadCheckpointKey(dir string) (*CheckpointKey, error)

LoadCheckpointKey reads the private key file under dir/keys and rebuilds the full CheckpointKey.

func ParseCheckpointKey

func ParseCheckpointKey(skey string) (*CheckpointKey, error)

ParseCheckpointKey rebuilds a CheckpointKey from the note-format private key string. The note skey embeds the Ed25519 seed (base64(algEd25519 || seed)); the vkey is re-derived from it.

func (*CheckpointKey) NoteSigner

func (k *CheckpointKey) NoteSigner() (note.Signer, error)

NoteSigner returns the note.Signer Tessera expects for checkpoint signing.

func (*CheckpointKey) NoteVerifier

func (k *CheckpointKey) NoteVerifier() (note.Verifier, error)

NoteVerifier returns the note.Verifier for this key's checkpoints.

type Envelope

type Envelope = envelope.Envelope

Envelope is the parsed view of a stored envelope. Payload aliases the original envelope bytes — it is the exact signed span, never re-serialized.

func ParseEnvelope

func ParseEnvelope(env []byte) (*Envelope, error)

ParseEnvelope extracts the payloadType, the exact payload byte span, and the signature from stored envelope bytes using a span scanner — it never parse-and-reserializes the payload.

type EpochRecord

type EpochRecord struct {
	Epoch     uint64 `json:"epoch"`
	PID       int    `json:"pid"`
	StartedAt string `json:"started_at"` // RFC 3339 UTC
}

EpochRecord is the on-disk epoch file content.

func ReadEpoch

func ReadEpoch(dir string) (EpochRecord, error)

ReadEpoch returns the current epoch record, or (zero record, nil) if no epoch file exists yet.

type ExportOption

type ExportOption func(*exportOptions)

ExportRun writes a Week-1 behalf.sh/export/v1 file for one run, derived from the log (docs/export-format-v1.md). It is a read-only path over the log dir: no appender is started and no epoch is claimed.

The span rule end to end: leaf payload bytes come from the stored envelope bytes verbatim — the envelope's payload span is extracted with a span scanner and spliced into the leaf line unmodified, and the leaf signature is the emitter's original signature from the envelope, never re-signed. Only receipts covered by the published checkpoint are exportable. The head is signed by the log's checkpoint key, whose JWK is in the header key set.

True tile-directory verification (checkpoint + inclusion proofs under a new format string) is the next issue (ENG-7); this bridge keeps the Week-1 verifier and tamper suite working against log-derived exports. ExportOption configures ExportRun.

func WithHopTokens

func WithHopTokens(blobs *cas.Store) ExportOption

WithHopTokens makes the export carry the delegation hop tokens its receipts reference, read from the customer-held blob store (ENG-38).

It is an option rather than the default because the store is the customer's, not the log's: an export can legitimately be produced by someone holding the log directory and nothing else, and that export is still a valid export — just one whose chains cannot be re-verified offline. Silently producing a tokenless export from a caller who *did* have the store would be the bad outcome, so `behalf-log export` always passes this.

A hop whose token is missing from the store is skipped rather than fatal. The store is customer-held and may have been pruned, and an export that carries three of a run's four hop tokens is more useful than no export; the verifier reports the absent one as unchecked rather than as a break.

type Log

type Log struct {
	// contains filtered or unexported fields
}

Log is an open, appendable behalf log. One Log handle per process per dir; the newest Open fences all older holders (Q57).

func Open

func Open(ctx context.Context, dir string, checkpointSigner *CheckpointKey, opts Options) (*Log, error)

Open claims a new epoch for dir, opens the SQLite index, and starts the Tessera POSIX appender with checkpointSigner as the checkpoint key. Single-appender discipline rests on Tessera's in-process mutex plus POSIX lock file; the epoch file adds behalf's product-level fence on top: an older holder whose epoch has been superseded is refused (ErrFenced) on its next Append.

The caller must Close the log; Close flushes and waits for a checkpoint covering everything appended by this handle.

func (*Log) Append

func (l *Log) Append(ctx context.Context, envelope []byte) (*AppendResult, error)

Append durably appends one stored envelope (the DSSE-signed receipt bytes, built with BuildEnvelope) to the log and blocks until Tessera's future resolves. On the POSIX driver a resolved future means the entry is durably committed and integrated into the on-disk tiles (Q75), so a non-error return here is the full durability ack: bytes on disk, index assigned, receipt promise signed. The returned promise is not an inclusion proof.

Before appending, the receipt_id is checked against the persistent dedup window (index.db): a duplicate returns the original index, flagged, and is never appended twice (Q46).

func (*Log) BeginAppend

func (l *Log) BeginAppend(ctx context.Context, envelope []byte) (*Pending, error)

BeginAppend checks dedup, then queues envelope for sequencing and returns immediately. Sequential BeginAppend calls preserve log order; use Wait on each returned Pending (in any order) for the durability acks. This is the pipelined form of Append for bulk ingest.

func (*Log) Close

func (l *Log) Close(ctx context.Context) error

Close flushes outstanding appends, waits for a checkpoint covering them, stops the background tasks, and closes the index.

func (*Log) Epoch

func (l *Log) Epoch() EpochRecord

Epoch returns the epoch record this handle claimed at Open.

func (*Log) Key

func (l *Log) Key() *CheckpointKey

Key returns the checkpoint key this handle signs with.

func (*Log) ReadCheckpoint

func (l *Log) ReadCheckpoint(ctx context.Context) ([]byte, error)

ReadCheckpoint returns the latest published checkpoint bytes.

func (*Log) RegisterKey

func (l *Log) RegisterKey(jkt, jwkJSON string) error

RegisterKey records a public key JWK (JSON) under its RFC 7638 thumbprint so the export bridge can embed it in export headers.

func (*Log) TreeSize

func (l *Log) TreeSize(ctx context.Context) (uint64, error)

TreeSize returns the current integrated tree size.

func (*Log) WitnessCheckpoint

func (l *Log) WitnessCheckpoint(ctx context.Context) (*WitnessRecord, error)

WitnessCheckpoint runs one witnessing pass over the log's currently published checkpoint and returns the per-checkpoint record. It is the explicit form of what the background pass does, exposed so an operator (and the tamper suite) can submit on demand.

type LogCheckpoint

type LogCheckpoint struct {
	Origin string
	Size   uint64
	Root   []byte // RFC 6962 root hash
	Raw    []byte // the full signed note, verbatim
}

LogCheckpoint is the parsed, signature-verified published checkpoint.

func ParseLogCheckpoint

func ParseLogCheckpoint(ctx context.Context, dir string) (*LogCheckpoint, error)

ParseLogCheckpoint reads dir/checkpoint, verifies its signature against the log's verifier key (dir/keys/checkpoint.vkey), and returns the parsed contents. It is a read-only operation.

type Options

type Options struct {
	CheckpointInterval time.Duration
	BatchMaxAge        time.Duration
	BatchMaxSize       uint

	// Witness overrides the witness policy read from
	// <dir>/witnesses.json. Nil means "use the file, or no witnesses if
	// there is no file"; a non-nil policy with no witnesses disables
	// witnessing for this handle. See witness.go for the availability
	// mode (Q96): fail-open by default, and never blocking publication.
	Witness *WitnessPolicy

	// HTTPClient is used for witness submissions. Nil takes
	// http.DefaultClient.
	HTTPClient *http.Client
}

Options configures Open. Zero values take the defaults above.

type Pending

type Pending struct {
	// contains filtered or unexported fields
}

Pending is an in-flight append: the entry is queued (order across sequential BeginAppend calls is preserved by Tessera) but the durability ack has not resolved yet. Call Wait to block for the ack.

func (*Pending) Wait

func (p *Pending) Wait(ctx context.Context) (*AppendResult, error)

Wait blocks until the durability ack for this append resolves (durable commit + integration on POSIX), records the receipt in the index, and signs the receipt promise. Only the current epoch holder signs promises: if this handle has been fenced by a newer epoch, Wait returns ErrFenced and no ack or promise is produced, even though the entry itself may be durably in the log.

type Promise

type Promise struct {
	V         string `json:"v"`          // PromiseVersion
	ReceiptID string `json:"receipt_id"` // the promised receipt's ULID
	LeafHash  string `json:"leaf_hash"`  // hex, RFC 6962 leaf hash of the stored envelope bytes
	IssuedAt  string `json:"issued_at"`  // RFC 3339 UTC
	MMDSec    int    `json:"mmd_s"`      // PromiseMMDSeconds
}

Promise is the receipt promise statement — the CT SCT analogue (architecture D2/Q57). It is a signed commitment, returned synchronously with the append ack, that the log has durably committed the leaf and that a checkpoint covering it will publish within mmd_s seconds.

A promise is NOT an inclusion proof. It is redeemable against a published checkpoint: a verifier that holds a promise checks that a checkpoint of sufficient size exists and that the leaf is included under it. A promise that never becomes redeemable within the MMD is capture loss, receipted on recovery (Q57).

func NewPromise

func NewPromise(receiptID string, leafHash []byte, issuedAt time.Time) Promise

NewPromise builds the promise statement for a committed leaf.

func VerifyPromise

func VerifyPromise(pub ed25519.PublicKey, sp *SignedPromise) (Promise, error)

VerifyPromise checks sp's signature against pub over the exact statement bytes and returns the parsed promise. It verifies only the promise signature — a valid promise is not an inclusion proof and says nothing about checkpoint coverage; redeem it against a published checkpoint.

type SignedPromise

type SignedPromise struct {
	Statement []byte // the promise JSON, exactly as signed
	KeyID     string // RFC 7638 thumbprint of the checkpoint key's JWK
	Sig       []byte // Ed25519 over PAE(PayloadTypePromise, Statement)
}

SignedPromise carries the exact signed promise bytes plus the signature. Statement is the byte span that was signed (the span rule); it is never re-serialized.

func DecodeSignedPromise

func DecodeSignedPromise(line []byte) (*SignedPromise, error)

DecodeSignedPromise parses an encoded signed promise, extracting the statement byte span with a span scanner (never parse-and-reserialize).

func SignPromise

func SignPromise(priv ed25519.PrivateKey, keyid string, p Promise) (*SignedPromise, error)

SignPromise serializes p exactly once and signs those bytes with the checkpoint key (only the current lock-holder signs promises — Q57). The returned SignedPromise is not an inclusion proof; it is an SCT-style commitment redeemable at checkpoint publication.

func (*SignedPromise) Encode

func (sp *SignedPromise) Encode() []byte

Encode renders the signed promise as one JSON line, splicing the signed statement bytes verbatim (the span rule):

{"promise":<statement verbatim>,"sig":{"keyid":<jkt>,"sig":"<b64std>"}}

type WitnessPolicy

type WitnessPolicy struct {
	// FailOpen: publish checkpoints even when the witness policy cannot be
	// satisfied. Defaults to DefaultWitnessFailOpen (true) when nil.
	FailOpen *bool `json:"fail_open,omitempty"`
	// TimeoutMS bounds one witnessing pass. Zero takes
	// DefaultWitnessTimeout.
	TimeoutMS int64 `json:"timeout_ms,omitempty"`
	// Quorum is how many witnesses must cosign for the policy to be
	// satisfied. Zero means all of them.
	Quorum int `json:"quorum,omitempty"`
	// Witnesses is the configured witness set. Empty disables witnessing
	// entirely, which is recorded as such rather than silently skipped.
	Witnesses []witness.Ref `json:"witnesses"`
}

WitnessPolicy is the log's witnessing configuration. It lives at <log dir>/witnesses.json and is loaded at Open; Options.Witness overrides it in process.

func LoadWitnessPolicy

func LoadWitnessPolicy(dir string) (*WitnessPolicy, error)

LoadWitnessPolicy reads <dir>/witnesses.json. A missing file is not an error: it means no witnesses are configured, which is a legal (and recorded) state.

func (*WitnessPolicy) Enabled

func (p *WitnessPolicy) Enabled() bool

Enabled reports whether any witness is configured.

func (*WitnessPolicy) FailOpenValue

func (p *WitnessPolicy) FailOpenValue() bool

FailOpenValue resolves the fail-open policy, applying the default.

func (*WitnessPolicy) QuorumValue

func (p *WitnessPolicy) QuorumValue() int

QuorumValue resolves the quorum, applying the "all of them" default.

func (*WitnessPolicy) Timeout

func (p *WitnessPolicy) Timeout() time.Duration

Timeout resolves the pass timeout, applying the default.

type WitnessRecord

type WitnessRecord struct {
	Time   string `json:"time"`
	Origin string `json:"origin"`
	Size   uint64 `json:"size"`
	Root   string `json:"root"` // lowercase hex
	// Outcome is the aggregate: `cosigned` (quorum met), `refused` (at
	// least one witness applied the safety rule and said no — this
	// dominates, because it is a finding about the log), `not-cosigned`
	// (quorum not met, no refusal), or `no-witnesses` (none configured).
	Outcome string `json:"outcome"`
	// Class and Reason carry the refusal in the verifier's vocabulary
	// (docs/export-format-v1.md): class `truncation` or `chain`, reason
	// `smaller-size`, `same-size-different-root` or `inconsistent-proof`.
	Class  string `json:"class,omitempty"`
	Reason string `json:"reason,omitempty"`
	// Detail says why, in words, whenever the outcome is not `cosigned`.
	Detail string `json:"detail,omitempty"`
	// FailOpen, TimeoutMS and Quorum record the policy this checkpoint was
	// published under, so the record is self-describing years later.
	FailOpen  bool  `json:"fail_open"`
	TimeoutMS int64 `json:"timeout_ms"`
	Quorum    int   `json:"quorum"`
	// Cosigned counts witnesses that cosigned this checkpoint.
	Cosigned int `json:"cosigned"`
	// Witnesses is the per-witness detail, cosignatures included.
	Witnesses []witness.Result `json:"witnesses,omitempty"`
}

WitnessRecord is one checkpoint's witness outcome — the per-checkpoint record Q96 asks for. One line of witness/outcomes.jsonl.

func ReadWitnessRecords

func ReadWitnessRecords(dir string) ([]WitnessRecord, error)

ReadWitnessRecords reads every per-checkpoint witness record in a log dir, oldest first. A missing file yields no records and no error.

func WitnessDir

func WitnessDir(ctx context.Context, dir string, policy *WitnessPolicy) (*WitnessRecord, error)

WitnessDir submits the currently published checkpoint of the log in dir to its configured witnesses, without opening an appender (so it never fences the running log service — Q57). This is the path `behalf-log witness` uses.

func (*WitnessRecord) Cosignatures

func (r *WitnessRecord) Cosignatures() []string

Cosignatures returns the note signature lines held for this checkpoint.

Jump to

Keyboard shortcuts

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