Tags: record, fact, view, query
Lace rules reason about validated record bytes through explicit facts. This spec defines those record facts and the origin-aware views used when one side evaluates rules contributed by another side. The purpose is to keep record identity hash-based while giving policies a safe, explicit way to decide which local record facts a peer-authored rule may query.
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 letting them inspect local record facts outside the local query view for that peer.
Record identifiers, record fact predicates, fact views, query exposure, origin-aware record-fact resolution, and disclosure bounds for queryable facts.
Record identifiers are typed record hash text for a locally supported record definition. In the current H3 record definition these are:
B.<b64a>.H3
P.<b64a>.H3
S.<b64a>.H3
A record identifier denotes validated local record bytes only when
Have(P) 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.
The storage layer exposes record facts only from locally stored, hash-valid 010 records. Invalid records MUST NOT produce record facts.
| Predicate | Arity | Meaning |
|---|---|---|
Have(P) |
1 | validated record bytes for P are locally stored |
Field(P, Name, Index, Value) |
4 | one record field occurrence |
RecordLink(P, Name, Index, Data, Target) |
5 | one well-formed 010 record-link field occurrence |
BlobHash(P, H) |
2 | embedded Blob-record hash for a Plex record |
PlexHash(P, H) |
2 | embedded Plex-record hash for a Seal record |
All fact values are UTF-8 NFC text strings. An implementation MUST reject a fact value that is not valid UTF-8 NFC or that exceeds the configured tuple value limit of the evaluating Datalog profile.
A projection, or table view, is a presentation over these facts: choose field names and show one row per record identifier with those fields as columns. The projection model is explanatory and useful for porcelain, but protocol truth remains the explicit facts below.
Field represents singleton protocol fields and repeated
Plex extra headers in one ordered model. Name uses the
exact 010 header 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 app namespace |
Name |
Plex-record app-local name |
TAI |
Plex-record TAI text |
Signed-By |
Seal-record verifier text |
Signature |
Seal-record signature text |
| any Plex-record extra header name | one extra header occurrence |
Index is a decimal occurrence index for
Name within record P, starting at
0, with no leading zero except 0. Singleton
fields always use index 0. Repeated extra headers with the
same name use increasing indexes in canonical record order. A
Field fact is set membership; duplicate
same-name/same-value extra header occurrences remain distinguishable by
Index.
Group, App, Name,
TAI, Plex-record extra header fields,
RecordLink, and BlobHash exist for valid Plex
records and for valid Seal records by reference to the embedded Plex
record. Signed-By, Signature, and
PlexHash exist only for valid Seal records.
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(P,'Group',...) carries the Group path text;
coordinate text is syntax over Group, App, and
Name fields.
HavePlex records and Seal records embed inner record bytes. The outer record’s facts include selected facts by reference to those embedded records as described above.
Storing or validating an outer record does not by itself require the
store to expose Have(H) for the embedded Blob-record or
Plex-record hash H. Have(H) means the store
admits H as a locally stored record in its own right.
Implementations MAY admit embedded records separately, and thin-record
reconstruction may require that inner records are already known, but
independent sending or requesting of an inner record requires
Have(inner) in the relevant local fact view.
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 queryable by the rule origin/viewer.
An origin identifies where a policy module or selector operand came from for the current exchange plan. Origins are assigned by exchange setup or by a binding. A module MUST NOT be allowed to grant itself a different origin by declaring text inside the lacegram.
A viewer is the principal or side for which a query view is computed. A viewer MAY be a proven verifier, an exchange-local source label, a side label, or a binding-defined principal. Unauthenticated or peer-chosen labels SHOULD default to the least or empty query view unless local policy explicitly grants them a view.
The standard mental model for two peers Alice and Bob is:
Bob-authored rules on Bob: Bob's full local record facts
Alice-authored rules on Bob: Bob's Alice-queryable record facts
Alice-authored rules on Alice: Alice's full local record facts
Bob-authored rules on Alice: Alice's Bob-queryable record facts
The standard internal record-level query gate is:
| Predicate | Arity | Meaning |
|---|---|---|
CanQueryRecord(Viewer, P) |
2 | record P is queryable by Viewer in this
local context |
CanQueryRecord is a reserved profile predicate.
Peer-origin modules MUST NOT be able to define or override another
side’s CanQueryRecord facts.
Local policy exposes queryable records through local-only query/expose facets. Conceptually, a local policy may derive:
AllowQueryRecord(Viewer,P) :- Have(P), Field(P,'Group',_,'X').
The exact authoring surface for query/expose facets is defined by the exchange policy profile in 040. The required semantic property is:
peer-origin rules may inspect only records allowed by the local query view for
that peer/viewer.
When multiple active local query constraints apply to a viewer, default composition is intersection. A record is queryable only when every active local query constraint allows it.
Conceptual lowering for constraints 1..N is:
CanQueryRecord(Viewer,P) :-
AllowQueryRecord_1(Viewer,P),
AllowQueryRecord_2(Viewer,P),
...,
AllowQueryRecord_N(Viewer,P).
If there are no active local query constraints for a viewer, the default query 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 query constraint SHOULD narrow or preserve the query view by default.
When a local-origin module is evaluated on its own machine, record fact predicates resolve against the full local view unless local policy selects a narrower view.
When a peer-origin module is evaluated on a local machine, record
fact predicates resolve through
CanQueryRecord(Viewer,P):
Have(P) => Have(P) and CanQueryRecord(Viewer,P)
Field(P,N,I,V) => Field(P,N,I,V) and CanQueryRecord(Viewer,P)
RecordLink(P,N,I,D,T) => RecordLink(P,N,I,D,T) and CanQueryRecord(Viewer,P)
BlobHash(P,H) => BlobHash(P,H) and CanQueryRecord(Viewer,P)
PlexHash(P,H) => PlexHash(P,H) and CanQueryRecord(Viewer,P)
This transformation may be implemented by rewriting rules, by a faceted fact resolver, or by an internal representation with owner/viewer arguments. The observable semantics MUST be the same.
The transformation applies to the entire peer-origin module, including helper predicates. A peer-origin helper predicate MUST NOT be able to read raw local record facts and then expose the result to exported selection predicates.
Negation and cardinality use the same resolved fact 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 Alice-queryable view, not Bob’s raw store:
Field(Q,'Group',_,'Y')
not Field(Q,'Group',_,'Y')
Cardinality(Field(Q,'Group',_,'Y'),'>=','10')
A query view is a declassification boundary. Anything included in a peer’s query view should be treated as queryable by that peer’s contributed rules. 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, RecordLink,
BlobHash, or PlexHash fact is exposed for
record P, then the record identifier P is also
disclosed by that fact. Profiles SHOULD expose
CanQueryRecord consistently with non-Have
record facts, but 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 query 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.