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
- Variables
- func BuildEnvelope(payloadType string, payload []byte, keyid string, sig []byte) []byte
- func ConsistencyProof(ctx context.Context, dir string, from, to uint64) ([][]byte, error)
- func ExportRun(ctx context.Context, dir, runID string, w io.Writer, opts ...ExportOption) error
- func InclusionProof(ctx context.Context, dir string, index, size uint64) ([][]byte, error)
- func LoadEmitterKeys(dir string) (map[string]string, error)
- func LoadVerifierKey(dir string) (string, error)
- func Reindex(ctx context.Context, dir string) (*index.Stats, error)
- func SaveCheckpointKey(dir string, k *CheckpointKey) error
- func SaveWitnessPolicy(dir string, p *WitnessPolicy) error
- func VerifyConsistency(fromSize, toSize uint64, pf [][]byte, fromRoot, toRoot []byte) error
- func VerifyInclusion(index, size uint64, leafHash []byte, pf [][]byte, root []byte) error
- func WitnessConfigPath(dir string) string
- func WitnessOutcomesPath(dir string) string
- func WitnessedCheckpointPath(dir string) string
- type AppendResult
- type BundleReader
- type CheckpointKey
- type Envelope
- type EpochRecord
- type ExportOption
- type Log
- func (l *Log) Append(ctx context.Context, envelope []byte) (*AppendResult, error)
- func (l *Log) BeginAppend(ctx context.Context, envelope []byte) (*Pending, error)
- func (l *Log) Close(ctx context.Context) error
- func (l *Log) Epoch() EpochRecord
- func (l *Log) Key() *CheckpointKey
- func (l *Log) ReadCheckpoint(ctx context.Context) ([]byte, error)
- func (l *Log) RegisterKey(jkt, jwkJSON string) error
- func (l *Log) TreeSize(ctx context.Context) (uint64, error)
- func (l *Log) WitnessCheckpoint(ctx context.Context) (*WitnessRecord, error)
- type LogCheckpoint
- type Options
- type Pending
- type Promise
- type SignedPromise
- type WitnessPolicy
- type WitnessRecord
Constants ¶
const ( DefaultCheckpointInterval = 1 * time.Second DefaultBatchMaxAge = 250 * time.Millisecond DefaultBatchMaxSize = 256 )
Defaults per architecture Q30/D3.3.
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.
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.
const EnvelopeVersion = envelope.Version
EnvelopeVersion is the version string stamped on every stored envelope.
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).
const PayloadTypePromise = "application/vnd.behalf.promise+json"
PayloadTypePromise is the DSSE payloadType for receipt promises.
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).
const PromiseVersion = "behalf.sh/promise/v1"
PromiseVersion is the promise statement version string.
Variables ¶
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 ¶
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 ¶
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 InclusionProof ¶
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 ¶
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 ¶
LoadVerifierKey reads the public verifier key file under dir/keys.
func Reindex ¶
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 ¶
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 ¶
VerifyInclusion checks an inclusion proof for leafHash at index in a tree of the given size and root.
func WitnessConfigPath ¶
WitnessConfigPath is where LoadWitnessPolicy looks.
func WitnessOutcomesPath ¶
WitnessOutcomesPath is the per-checkpoint record file for a log dir.
func WitnessedCheckpointPath ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
ReadCheckpoint returns the latest published checkpoint bytes.
func (*Log) RegisterKey ¶
RegisterKey records a public key JWK (JSON) under its RFC 7638 thumbprint so the export bridge can embed it in export headers.
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 ¶
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.