Skip to content
134 changes: 134 additions & 0 deletions docs/consumer-interface-contract-v1-observed.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: CC-BY-SA-4.0
= VeriSimDB Consumer Interface Contract — Observed v1 Boundary

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register the new documents in the documentation index

docs/INDEX.adoc explicitly describes itself as the table of contents for everything under docs/, but none of the three documents added here is listed there. The only reference from the handover to the consumer contract is an inline literal rather than an AsciiDoc link, so readers following the repository's official documentation navigation cannot discover this contract or either review document.

Useful? React with 👍 / 👎.

:revdate: 2026-10-29
:toc: left

[IMPORTANT]
====
This is a **source-derived description of current behavior**, not an
advertisement of cross-modal serializability, federation authenticity,
verified proof-carrying transitions or arbitrary modality cardinality.
It must be checked against each release and against the deployed feature
set. Do not infer guarantees from the OpenAPI schema alone.
====

== Endpoint and versioning

* HTTP Axum API listens on \`ApiConfig.host:port\` (defaults \`[::1]:8080\`).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Report the executable's wildcard bind default

When the server binary starts without VERISIM_HOST, main.rs constructs ApiConfig with [::], bypassing the [::1] value from ApiConfig::default; with authentication also disabled by default, this distinction determines whether the API is remotely reachable over IPv6. Describing the operational default as loopback can therefore lead deployments to expose an unauthenticated listener unintentionally.

Useful? React with 👍 / 👎.

\`mount_with_prefix\` exposes the routes both at the legacy root and at
\`/api/v1\`; the prefix is configurable.
* GraphQL: \`POST /graphql\` and \`GET /graphiql\`; gRPC Tonic service on
separate configurable port, default 50051. Authentication of gRPC is
Comment on lines +20 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify gRPC availability when TLS is enabled

In deployments of the stock binary with both VERISIM_TLS_CERT and VERISIM_TLS_KEY set, main.rs calls serve_tls, and that function starts only the Axum TLS listener; the configurable Tonic server is started exclusively by the non-TLS serve path. Thus clients following this contract will find no service on port 50051 in an otherwise supported TLS configuration, so the endpoint description should document this exception rather than presenting gRPC as uniformly available.

Useful? React with 👍 / 👎.

not established by the HTTP middleware.
* OTP orchestration is a separate service (Bandit port default 4080);
its RustClient defaults to \`http://localhost:8080/api/v1\`.
* Neither the current JSON schema URLs nor Octad payloads carry a
negotiable modality-profile version. \`OctadSchema\` / VCLS v1 in
VCL-UT are fixed eight-slot contracts; generic profiles are proposed
terminology/architecture, NOT implemented wire capability.

== Supported core HTTP operations

[cols="3,3,4",options="header"]
|===
|Route |Current semantics |Important guarantee boundary
|\`POST /octads\` |Create subject, server-generated ID, 201 |Flattened payload accepted by runtime, *not* the nested published OctadInput schema; default storage is in-memory
|\`GET /octads/{id}\` |Read current subject, 200 or 404 |Results may be incomplete if a modality store is inconsistent

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge State that GET omits all modality payloads

The runtime OctadResponse contains only the ID, timestamps/version, has_* flags, and version/provenance counts; it drops the loaded document, embedding, tensor, semantic annotation, graph data, spatial data, and metadata. Consequently GET /octads/{id} cannot actually return the current subject content, even when every store is consistent, so consumers need this response-shape incompatibility called out rather than only a warning about inconsistent stores.

Useful? React with 👍 / 👎.

|\`PUT /octads/{id}\` |Merge supplied fields into existing subject, 200 or 404 |Not a replace; omissions retain earlier modalities

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the asymmetric title/body update semantics

For an existing document, this is not a field-level merge: OctadRequest::to_octad_input turns a title-only request into a replacement document with an empty body, while a body-only request creates no document input and leaves the old document untouched. A consumer following this contract can therefore erase content when attempting to update only the title; describe this asymmetry or require both fields rather than promising that supplied fields are merged.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document false modality flags in PUT responses

When an update omits an already-populated modality, storage retains it in modality_status, but InMemoryOctadStore::update returns an Octad containing only modalities written by that request, and OctadResponse::from derives every has_* flag from those optional payload fields. A document-only update to an entity that already has a vector therefore returns has_vector: false even though a subsequent GET reports it as present; consumers using the PUT response as the current merged state need this inconsistency called out.

Useful? React with 👍 / 👎.

|\`DELETE /octads/{id}\` |Remove subject from visible registry, 204 or 404 |Does not prove tombstone, total physical deletion or expungement
|\`GET /octads\` |Pagination |No cross-witness snapshot isolation guarantee demonstrated
|\`GET /search/text\` |Tantivy full text |Index and physical modality must be checked for freshness
|\`POST /search/vector\` |Similarity |Returns embedding search, not meaning equivalence
|\`GET /search/related/{id}\` |Graph traversal |Graph edges may outlive deleted registry rows
|\`GET /drift/entity/{id}\` |Per-entity score and computability flags |Score not an identity or authority proof
|\`POST /normalizer/trigger/{id}\` |Regeneration of selected modality |Side effects and ranked authority; not a guaranteed safe proof-preserving repair
|\`GET /provenance/{id}\`, \`/verify\` |Hash-chain retrieval and verification |No external signed root or certified actor identity

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not advertise the stubbed provenance verify route

For any existing entity, provenance_verify_handler returns "chain_valid": true without calling verify_chain, including when the entity has no provenance or its persisted chain is corrupt. Only the retrieval handler performs verification, so presenting /verify as hash-chain verification can cause consumers to accept fabricated integrity success.

Useful? React with 👍 / 👎.

|\`POST /provenance/{id}/record\` |Append provenance event |Caller-provided actor text must not be treated as attestation

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document the record route's 500 for unknown subjects

For POST /provenance/{id}/record with a well-formed but nonexistent ID, OctadStore::update returns OctadError::NotFound, but this handler maps every store error to ApiError::Internal; the observed response is therefore a generic 500 rather than the 404 that the contract associates with an unknown subject. Clients may otherwise treat this permanent missing-entity condition as a retryable server failure, so the route-specific behavior should be stated until the handler maps NotFound correctly.

Useful? React with 👍 / 👎.

|\`POST /vcl/execute\` |Subset SELECT, SEARCH, INSERT, DELETE, SHOW, COUNT, EXPLAIN |TypeLL/VCL-UT request is best-effort and never blocks an action if unavailable
|\`POST /federation/query\` |HTTP query fan-out to registered peers |Not atomic identity federation or independent witness verification

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clarify that TypeLL results never block VCL mutations

This qualifier understates the observed behavior when TypeLL is reachable: vcl_execute_handler executes INSERT or DELETE before merely attaching the returned safety metadata, and it also proceeds when the checker returns a non-success status. Thus no TypeLL verdict—including a level indicating an unsafe transition—can block the action, not just checker unavailability; consumers relying on a live validator as an admission gate can still perform rejected mutations.

Useful? React with 👍 / 👎.

|===

Other endpoints exist for planners, transactions, proofs, spatial searches,
and learning-loop attempts; their presence must not be read as a
distributed transaction or proof-validity guarantee. Inspect the relevant
handler, feature flags, and caller-specific contract.

== Observed payload example

The Rust HTTP \`OctadRequest\` is *flattened*:

[source,json]
----
{
"title": "Witness example",
"body": "Document content",
"embedding": [0.1, 0.2, 0.3],
"types": ["https://schema.org/Thing"],
"relationships": [["related_to", "other-subject-id"]],
"metadata": {"source_label": "unverified"}
}
----

The embedding length must match the configured dimension (default 384);
the three-dimensional example needs a development server configured to
dimension 3. Omit \`embedding\` for a dimension-independent example.

\`graph\`, \`document\`, \`vector\` nested root keys from
\`connectors/shared/json-schema/octad-input.json\` are **not** runtime
HTTP request fields. On the base revision, serde ignores them, silently
losing their values. Proposed PR #334 rejects such payloads instead.
Comment on lines +76 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include semantic among silently ignored schema fields

A schema-conforming payload such as {"semantic":{"types":[...]}} is also silently discarded on the reviewed revision: OctadRequest has no semantic field and expects top-level types instead. Because this warning enumerates only graph, document, and vector, clients may reasonably infer that the schema's nested semantic object is supported and receive a successful response with their annotations missing.

Useful? React with 👍 / 👎.

Comment on lines +78 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Describe nested payload rejection as current behavior

PR #334 is already the immediate parent of this revision, where OctadRequest has #[serde(deny_unknown_fields)] and nested schema payloads return 422. Presenting rejection as merely proposed leaves consumers unable to determine the actual wire behavior and incorrectly suggests that current requests may succeed while losing values; document rejection as the observed behavior and mark #334 as landed.

Useful? React with 👍 / 👎.

Consumers should not depend on either behavior until the versioned
contract inconsistency is resolved.

== Errors and failure states

[cols="1,4",options="header"]
|===
|Status |Observed meaning
|400 |Explicit \`ApiError::BadRequest\` / syntactic failures
|401 |Authentication middleware enabled, credentials absent/invalid
|403 |RBAC authorization denial, or federation registration without configured PSK
|404 |Unknown subject or endpoint
|413 |Oversized request body, under body-limit middleware
|422 |Axum JSON deserialization rejection (including unknown input keys after proposed PR #334)
|429 |Auth rate limiter (when enabled)
Comment on lines +89 to +94

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge State that the server cannot enable authentication

For deployments of the stock server binary, these authentication-dependent statuses are not merely conditional on configuration: serve() obtains its state from AppState::new_async, which always installs AuthState::default() with enabled = false, while neither ApiConfig nor main.rs exposes auth settings or API-key registration. Consequently operators cannot enable the middleware through the advertised executable at all, so describing 401/RBAC/limiting as available “when enabled” obscures that every request remains unauthenticated unless a separate embedding application constructs and mutates AppState itself.

Useful? React with 👍 / 👎.

|500 |Store/serialization/runtime error; generic client message for \`ApiError::Internal\`
|501 |Explicit \`ApiError::NotImplemented\`
|503 or startup failure |Do not assume readiness; persistent WAL replay failure is non-fatal on \`main\`, proposed PR #333 makes it fatal before listener start

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reflect the landed fail-closed WAL recovery

This no longer describes the reviewed revision: commit c5e6ac8 is this commit's parent, and its AppState::new_async returns an error when replay_wal fails, preventing the listener from starting; its replay implementation also pairs each intent with its own commit marker. Presenting that behavior as merely proposed can mislead operators about whether ambiguous persistent state will be served, and the other references to PR #333 as unmerged should be updated at the same time.

Useful? React with 👍 / 👎.

|===

No stable machine-readable distinction currently exists for all:
identity-conflict, stale witness, untrusted source, rejected VCL
transition, unauthorised expungement, recovery-required, and
federation uncertainty. These must be added in a versioned contract,
with explicit refusal/error responses rather than fabricated success.

== Guarantees that are *not* established

* No mandatory persistent store: compile \`verisim-api\` with feature
\`persistent\` and configure an actual writable persistence directory
to enable on-disk modality stores and WAL.
* No atomic all-or-nothing durable commit across every redb file and
Tantivy, nor proven crash-safe index recovery.
* No signed source authority, independent witness attestation, or
conflict-resolution legitimacy guarantee.
* No mandatory VCL-UT admission gate before mutations.
* No general merge/split/retraction/tombstone/approved-expungement API
semantics in the reviewed v1 lifecycle.
* No arbitrary-arity profiles, custom modality plugin negotiation or
quandle payload on the current Octad wire contract.
* No verified neural embedding computation: supplied embeddings and
deterministic feature hashes are supported; this does not imply a
trained neural model.
* No claim that \`verisimdb-data\` JSON snapshots are canonical subject
storage or a persistent replica.

== Changes under review

* PR #333: committed-intent WAL replay, fail closed on ambiguity.
* PR #334: refuse silently dropped Octad JSON fields.
* PR #335: unrelated strict-Clippy baseline fixes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark PR #335 as already merged

The reviewed commit's immediate parent is 8af7bde, whose subject is the landed fix(ci): remove pre-existing unused RAM promotion fields (#335). Listing #335 under changes still under review and then stating that none of them is merged gives readers an incorrect CI-baseline status and may prompt duplicate remediation work; record it as landed rather than proposed.

Useful? React with 👍 / 👎.

* PR #336: apply HTTP authentication middleware after GraphQL/federation
merge (when authentication is configured).

None is a released guarantee until merged, tested and versioned.
51 changes: 51 additions & 0 deletions docs/reviews/2026-10-29-hypatia-verified-handover.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: CC-BY-SA-4.0
= Verified Downstream Handover: Hypatia / VeriSimDB
:revdate: 2026-10-29

Scope: **only confirmed interfaces, incompatibilities, changed
assumptions and PR links**. This is not a VeriSimDB roadmap and does not
prescribe Hypatia's architecture.

* The current Rust HTTP API is mounted at both the root and, by
default, \`/api/v1\`. \`POST /octads\` accepts flattened fields such as
\`title\`, \`body\`, \`embedding\`, \`types\`, \`relationships\`, \`tensor\`,
\`spatial\`, \`provenance\`, \`metadata\`; it does not accept the nested
\`OctadInput\` shape as authored in the shared JSON Schema.
*Relevant change:* https://github.com/hyperpolymath/verisimdb/pull/334.
* The *fixed* implementation has eight slots, but any one subject may
populate fewer. Temporal is created on write; the runtime does not
negotiate modality profiles or support ninth/custom modalities.
The accepted generic term is **modality profile**, distinct from
*modality set* (present witnesses) and *consonance subject* (identity).
See \`docs/modality-profile.adoc\` and VCL-UT ADR 0007.
* \`/vcl/execute\` is a restricted Rust interpreter. TypeLL VCL-UT
preflight is best-effort: validator unavailability does *not* block
INSERT/DELETE. Neither proof carrying nor transition admissibility
may be assumed without an explicit upstream contract change.
* Drift reports include per-component \`computable\` status. A lower
drift score or auto-regeneration result is not proof of identity
equivalence or legitimate source authority.
* Persistent feature enables per-modality redb/Tantivy stores and WAL;
it does not demonstrate atomic cross-modal crash recovery. WAL
status-rebuild error is non-fatal on \`main\`, proposed fail-closed
behavior is https://github.com/hyperpolymath/verisimdb/pull/333.
* DELETE currently removes visible status and some modality content,
but is not a fully specified tombstone, retraction, or authorised
expungement. No consumer should interpret 204 as complete physical
erasure or retroactive provenance removal.
* Federation in Rust is peer registration plus HTTP search/list fan-out,
not a proof of independent witnesses or a distributed identity
transaction. Peer trust is initially 1.0 and result drift flag is
populated as false. Do not treat it as an independently validated
consonance result.
* GraphQL/federation route authentication is under proposed repair in
https://github.com/hyperpolymath/verisimdb/pull/336. The default
AuthState is disabled, and gRPC is separate from the Axum middleware.
* \`hyperpolymath/verisimdb-data\` is a distinct git-backed scan/dispatch
JSON dataset and ABI dogfood repository, not an interchangeable
VeriSimDB persistent-modal store. Historical Hypatia scan integration
documents there do not prove the live Rust API is integrated.

Use \`docs/consumer-interface-contract-v1-observed.adoc\` for
operation-level details. All proposed PRs are draft and must not be
considered deployed, released or merged until verified.
Loading
Loading