Tags: record, fact, view, exposure
Lace rules reason about validated record bytes through explicit facts. This spec defines those facts and the origin-aware views used when one side evaluates peer-contributed rules. It keeps record identity hash-based while giving policies an explicit way to decide which local record facts a peer-authored rule may inspect.
This document is for storage, Datalog, and exchange-policy implementors. After reading it, an implementor should be able to expose facts for validated 010 records and evaluate peer-origin rules without exposing local record facts outside that peer’s exposure view.
Record identifiers, record fact predicates, local store admission order, fact views, exposure, origin-aware record-fact resolution, and disclosure bounds.
Record identifiers are typed record hash text for a locally supported record definition. For H3 these are:
B.<b64a>.H3
P.<b64a>.H3
S.<b64a>.H3
A record identifier denotes validated local record bytes only when
Have(Record) is present in the relevant fact view.
Unsupported record hash formats MAY appear in peer advertisements or
record-link targets, but they MUST NOT become local record facts unless
their bytes validate under a locally supported record definition.
A local record-fact view exposes facts only from admitted records that fully validate under 010 and every referenced suite requirement. This includes record structure, outer and embedded hashes, field rules, and Seal-record Mark verification under 009. A merely hash-matching but otherwise invalid record MUST NOT produce record facts.
| Predicate | Arity | Meaning |
|---|---|---|
Have(Record) |
1 | validated record bytes for Record are locally
stored |
Field(Record, name, index, value) |
4 | one record field occurrence |
RecordLink(Record, name, index, data, Target) |
5 | one well-formed 010 record-link field occurrence |
ContainsRecord(Outer, Contained) |
2 | record Contained occurs at any canonical nesting depth
inside admitted record Outer |
All fact values are UTF-8 NFC text strings. Projected field names and values are exact; each originating name/value pair jointly satisfied the 010 complete field-line bound. The name and value remain separate fact values, and neither is truncated or omitted. A valid record’s facts are projected completely.
A local fact view supplies admission order through the 030 expression
AdmitOrder(Record). It is available only when
Have(Record) is present in that view. The order comes from
first admission; storing the same hash again does not change it.
Admission order is not derived from 010 bytes, hash-significant, a wire fact, a record identity, or a public cursor. It reveals only relative order in one local store. Protocol identity remains the record hash.
Compare(AdmitOrder(...),...) and
TopK(...,PartitionBy(...),AdmitOrder(...),...) use this
same visible local order. Origin-aware visibility is resolved before
TopK constructs partitions or ranks candidates. A candidate
with no visible independently admitted Have row has no
order value and is excluded before partition construction.
Peer-origin evaluation applies exposure gating to every record used
by AdmitOrder. A missing, hidden, unsupported,
advertisement-only, or embedded but independently unadmitted hash has no
admission-order value. Hidden records cannot create a partition,
displace a retained fact, add candidate work, or cause a peer-origin
resource-limit failure.
A projection, or table view, presents these facts as one row per record identifier with selected fields as columns. The projection model is useful for porcelain, but protocol truth remains the explicit facts below.
Field represents singleton protocol fields and repeated
Plex extra fields in one ordered model. Name uses the exact
010 field name where one exists. The current H3 record definition uses
these field names:
| Name | Meaning |
|---|---|
Type |
record type: B, P, or S |
Data-Length |
Blob-record data length as decimal text |
Group |
Plex-record group path |
App |
Plex-record application-contract path |
Name |
Plex-record app-local hierarchical name |
TAI |
Plex-record TAI text |
By |
Seal-record canonical 009 verification-key text |
Mark |
Seal-record Mark text |
| any Plex-record extra field name | one extra field occurrence |
Index is an ASCII decimal occurrence index for one field
name within the projected outer record, starting at 0, with
no leading zero except 0. Inherited Plex fields on a Seal
preserve the embedded Plex occurrences and indexes while using the Seal
hash as Record. Singleton fields always use index
0. Repeated extra fields with the same name use increasing
indexes in canonical record order. A Field fact is set
membership; duplicate same-name/same-value extra field occurrences
remain distinguishable by Index.
Group, App, Name,
TAI, Plex-record extra fields, and RecordLink
exist for valid Plex records and for valid Seal records by reference to
the embedded Plex record. By and Mark exist
only for valid Seal records. In particular, a Seal with canonical public
key text emits Field(Seal,'By','0',by_value).
Data-Length exists for Blob records and by reference to the
embedded Blob record for Plex and Seal records.
RecordLink is derived only from well-formed 010
record-link fields. Malformed record-link fields remain
Field facts but produce no RecordLink
facts.
Composite coordinates are not record facts.
Field(Record,'Group',...) carries the Group path text;
coordinate text is syntax over Group, App, and
Name fields.
ContainsRecord(Outer,Contained) is the complete
canonical-containment projection for one admitted outer record. For H3
records it emits exactly:
| Admitted outer kind | Facts |
|---|---|
| Blob | none |
| Plex | ContainsRecord(Plex,Blob) |
| Seal | ContainsRecord(Seal,Plex) and
ContainsRecord(Seal,Blob) |
Both arguments are record identifiers. Contained may
occur at any canonical nesting depth; callers do not recursively close
the relation. The relation carries no depth, path, role, or occurrence
information. It includes only records nested by the validated record
definition, not RecordLink targets or record-shaped bytes
inside Blob data.
This H3 projection is immutable for the H3 record definition. A future record definition with a different containment shape uses its own record-format suffix and explicitly defines its complete projection.
ContainsRecord(Outer,Contained) requires admitted,
validated Outer, but does not imply
Have(Contained). Admitting an outer record does not
independently admit, expose, load, advertise, request, send, or order a
contained record, assign it a StoreRecordId, or make
load-by-hash succeed for it. Have(Contained) exists only
after that exact hash is admitted through the ordinary record-admission
path. Query and policy evaluation never promote a contained hash into
Have and remain side-effect-free over one coherent
snapshot.
A store MAY privately materialize or deduplicate contained canonical
bytes. That physical state is not Datalog state and does not make an
unadmitted contained record independently loadable. If the contained
hash is admitted later, the store may reuse those bytes while adding the
ordinary admission entry and StoreRecordId. Independent
sending requires Have(Contained) in the relevant local fact
view; requesting an independently advertised contained hash requires the
ordinary advertisement, missing-record, and policy gates.
ContainsRecord may bind the checked hash used by that
request policy.
A fact view is the set of record facts available to one Datalog module during one evaluation snapshot. Fact views are explicit evaluation inputs. A Datalog engine MUST NOT read hidden wall-clock time, fetch record bytes from peers, or consult mutable external state except through explicit fact sources supplied to the current snapshot.
There are two important record-fact views:
Record facts are never ambient for peer-origin modules. A record-fact lookup is resolved in the context of:
fact owner, rule origin/viewer, evaluation snapshot
The fact owner controls which records are exposed to the rule origin/viewer.
An origin identifies which exchange side contributed a policy module
operand for the current exchange plan. In standard two-party interlace,
origins/viewers are the exchange-local side labels side0
and side1. Setup or the binding assigns side labels before
rule evaluation. A module MUST NOT be allowed to grant itself a
different origin by declaring text inside the lacegram.
A viewer is the side for which a peer exposure view is computed. Side
labels are coordination labels, not By values, authority,
capabilities, random tie-breakers, or durable peer names. They are not
source-authorable terms in standard policy. Policy that needs key
evidence uses explicit proof or evidence such as
PeerConfidential(peer_key), Here(local_key),
record Marks, or application records.
The standard mental model for two sides is:
side0-authored rules on side0: side0's full local record facts
side1-authored rules on side0: side0's side1-exposed record facts
side1-authored rules on side1: side1's full local record facts
side0-authored rules on side1: side1's side0-exposed record facts
Exposure is whole-record declassification for rule evaluation. If a
local policy exposes record P to a viewer such as
side1, peer-origin rules from that side may inspect the
standard record facts for P through the viewer’s peer
exposure view. Lace has no field-level privacy boundary inside an
exposed record. Exposure specific to a By value must be
expressed with explicit key proof or evidence; exposing to
side1 is not the same as exposing to that value.
Exposure is not the same as active convergence. Exposing
P lets peer-origin rules reason over P as
local dynamic scope; it does not by itself advertise, request, transfer,
or store P. Active convergence is selected by exchange
policy in 040 and executed by interlace in 050.
Local policy exposes whole records through the 040
ExposeRecord/1 facet. The exposure gate is not
source-addressable: policy modules MUST NOT define, inspect, or override
it. Peer-origin rules may inspect only records in their peer exposure
view.
When multiple active local exposure constraints are composed before becoming one 040 operand, default composition is intersection. A record is exposed only when every active local exposure constraint allows it. If there are no active local exposure rules for a peer-origin operand, that peer’s exposure view is empty unless a profile explicitly defines a different local default.
A local policy MAY explicitly construct a union or expanding view, but expansion MUST be explicit local policy. Adding a local exposure constraint SHOULD narrow or preserve the peer exposure view by default.
Local-origin modules use the full local view. Peer-origin modules use
only their peer exposure view. This rule applies to every record-fact
predicate, every AdmitOrder operand, and the entire module
including helper predicates.
Negation and cardinality use the same view as positive atoms. For
example, if Alice’s module is evaluated on Bob and Bob did not expose
Group Y to Alice, then all of these range over Bob’s peer
exposure view for Alice:
Field(Other,'Group',_,'Y')
not Field(Other,'Group',_,'Y')
Cardinality(Field(Other,'Group',_,'Y'),'>=','10')
A peer exposure view is a declassification boundary. Anything in that view should be treated as inspectable by that peer’s contributed rules at record granularity. If that is not acceptable for a record, do not expose the record to that viewer. The peer may learn Datalog-expressible properties of that view through visible exchange behavior, including requests, advertisements, summaries, timing, negation, and cardinality.
If a Field or RecordLink fact is exposed
for record P, then the record identifier P is
also disclosed by that fact. Exposing Outer also discloses
every contained hash in its ContainsRecord rows, but does
not independently expose Contained; reading its fields or
admission order still requires independently visible
Have(Contained).
For peer-origin evaluation, hidden Outer records MUST
NOT contribute containment facts, candidate work, resource-limit
failures, or other observable policy behavior. Stores remain
exposure-neutral; origin-aware evaluation owns this boundary above
storage.
Standard peer exposure views MUST apply the whole-record exposure
gate to every record-fact predicate, not only Have; each
exposed fact independently discloses any record identifiers it
contains.
The intended bounded disclosure property for peer-origin rules is:
Holding local-authored policy fixed, peer-origin rules can influence a side's
observable exchange behavior only through that side's peer exposure view for that peer,
public runtime facts, peer advertisement facts, and intrinsic protocol facts.
Known-hash possession is an intrinsic protocol fact in the current
interlace model: if a peer advertises a known hash P,
request or non-request may reveal whether the local side already has
P. Policy or exchange-plan identifiers may also be visible
during setup, diagnostics, or cache use. These disclosures are
acceptable in the current design and SHOULD be documented by bindings
that expose them.
This bound does not make unsafe local policy safe. Local-origin rules that make peer-visible behavior depend on private facts are declassification policy.