API reasoning internals
StandardApiReasoning is an opt-in, deterministic profile for JSON response
formats, GraphQL surface signals, and already-paired visibility comparisons.
It installs ontology definitions and Bayesian rules only. It performs no
network I/O and contains no planner, executor, credential, or verifier.
The separately feature-gated GraphQL surface review does not move transport into this profile. Its scanner runtime owns endpoint selection, up to three anonymous broker requests, bounded response classification, replay, and informational projection; committed normalized observations may then be consumed here like other HTTP/API evidence.
HTTP evidence ----------------------+
|
host-paired ApiVisibilityComparison +--> StandardApiReasoning
|
v
Supported hypotheses
|
v
host policy / later verifier
Profile contents
The profile contains nine concepts, six axioms, and seven rules. Installation
preflights rules on a cloned registry and ontology definitions on prospective
state, so it is atomic and idempotent. It can be installed alongside
StandardWebReasoning in either order without sharing rule identities.
Ontology
json ----------------is_a----> api-response-format
json-http-api ----is_a----> api-interface ----is_a----> application-interface
graphql-api ------is_a----/
ui-api-visibility-boundary -------------------is_a----> visibility-boundary
authorization-context-visibility-boundary ----is_a----> visibility-boundary
The exact concepts are:
application-interface
api-interface
api-response-format
json
json-http-api
graphql-api
visibility-boundary
ui-api-visibility-boundary
authorization-context-visibility-boundary
Deterministic rules
| Rule ID | Input | Conclusion | Strength |
|---|---|---|---|
api.response.json.media-type |
http.response.media-type-json-compatible = true |
api.response-format = json |
Weak |
api.surface.graphql.response-media-type |
http.response.media-type = application/graphql-response+json |
api.surface.kind = graphql-api |
Strong |
api.surface.graphql.route |
http.request.path-segment = graphql |
api.surface.kind = graphql-api |
Weak |
api.surface.json.paired-comparison |
Atomic JSON visibility comparison, either result | api.surface.kind = json-http-api |
Strong |
api.surface.graphql.paired-comparison |
Atomic GraphQL visibility comparison, either result | api.surface.kind = graphql-api |
Strong |
api.visibility.ui-api.paired-difference |
Atomic JSON or GraphQL UI/API difference | api.visibility.boundary = ui-api-visibility-boundary |
Weak |
api.visibility.authorization-context.paired-difference |
Atomic JSON or GraphQL authorization-context difference | api.visibility.boundary = authorization-context-visibility-boundary |
Weak |
Rule conditions and Bayesian policy likelihoods use the shared typed descriptors. The fixed values are deterministic reasoning weights, not empirically calibrated real-world probabilities. Treat the resulting posterior as a reproducible ranking signal until a labelled fixture corpus publishes calibration metrics such as Brier score, reliability buckets, precision, and recall.
The expression trace retains every candidate evidence ID that matched the
condition. Each API rule explicitly selects at most one deterministic Bayesian
contribution (MaxContributions(1)), preferring reliability, then observation
time, then stable evidence ID. The selected ID appears in the resulting belief
trail, so both the candidate set and posterior input remain explainable and
replayable. Other profiles keep the default Independent aggregation semantics
unless they opt into a bound themselves.
Persisted rule definitions that contain bounded aggregation must be loaded only by an engine version that understands that field. The current reader rejects unknown or misspelled likelihood fields, while a pre-aggregation binary cannot retroactively enforce that guarantee. Deployments therefore version rule payloads and reasoning-engine binaries together rather than downgrading a bounded profile into an older reader.
Conservative interpretation
JSON is a response representation, not a GraphQL detector. The HTTP producer
first validates and normalizes a media-type essence, then emits
http.response.media-type-json-compatible = true for json and +json
subtypes. That signal creates only the weak api.response-format = json
hypothesis. GraphQL requires the exact normalized
application/graphql-response+json media type, an exact graphql path-segment
signal, or an explicit host-paired GraphQL comparison. The reasoner does not
search raw Content-Type or URL strings.
An explicitly enabled GraphQL review adds stronger correlated protocol
evidence, but its conclusions remain conservative. A complete aliased
__typename control can establish an observed GraphQL surface. Anonymous
schema-root introspection is retained only after a distinct candidate and
replay both satisfy the bounded response contract. Neither observation is a
vulnerability or authorization claim, and the reasoner still cannot dispatch
the requests or raise either item above Informational / KnowledgeOnly.
A visibility difference is also not a vulnerability. UI/API differences may
be intentional product behavior, and authorization-context differences often
show that access control is working. The profile records a weak, supported
boundary hypothesis that deserves policy-aware review. It never emits IDOR,
data-leak, authorization-bypass, Confirmed, or Rejected claims.
An Equivalent comparison can still establish the declared API surface, but
it does not create a visibility-boundary hypothesis or a negative security
claim. Negative and confirmed lifecycle states belong to a future explicit
verifier operating under host authorization.
Comparison isolation
Every ApiVisibilityComparison produces one evidence record on a pseudonymous
comparison subject. The digest includes the opaque comparison, context, and
resource-scope handles together with surface, pair, result, and dimension. This
prevents observations from different principals, resources, dimensions, or
turns from entering the same subject snapshot.
The digest excludes observation time, producer provenance, and reliability; these are immutable evidence metadata rather than semantic comparison identity. The deterministic evidence ID still makes exact replay strict. If a producer recreates the same semantic identity with a different timestamp, component, or reliability, insertion fails with an evidence identity conflict instead of counting it as a second Bayesian observation.
The primary to_observation() path also returns a stable
api.visibility.resource-scope relation from that comparison subject to the
host-provided opaque resource EntityId. The relation is backed only by the
comparison evidence. Writing both records through
KnowledgeBase::insert_evidence_with_relation() is atomic and idempotent, so a
failed identity check cannot leave a detached comparison claim or a relation
without its observation.
The resource entity itself may be registered later. Knowledge-base entity
referential integrity is eventual by design so independent producers can write
in either order; the comparison-to-resource edge is queryable in the supplied
KnowledgeBase as soon as the bundle is accepted. Persisting it across process
failure remains a host responsibility.
to_evidence() is the detached, advanced alternative. A host using it must
retain and persist an equivalent resource mapping outside this bundle; the
reasoning profile cannot recover that association from the raw-value-free
evidence payload.
The profile never attempts an expression such as:
UI record exists AND API record exists
Such an expression would be unsound because independent records do not prove a shared principal or resource context. Only the atomic comparison predicates can activate the visibility rules.
Both ApiVisibilityComparison conversion paths and
HttpEvidencePolicy::with_reliability() reject zero reliability. Any nonzero
reliability is retained on evidence for audit and future policy. The current
rule engine does not multiply or otherwise scale its fixed Bayesian likelihoods
by that metadata, so a lower nonzero reliability does not currently reduce the
rule's posterior contribution.
Trust boundary
StandardApiReasoning assumes an authorized evidence-write boundary.
ApiVisibilityComparison validates the canonical host-paired contract, but
standard predicate names are public knowledge-base identifiers rather than
cryptographic attestations. A writer with direct knowledge-base access can
construct matching Evidence without using the comparison constructor.
The comparison digest is a pseudonymous deterministic identity, not an HMAC, signature, or producer attestation. Low-entropy handles may be guessable even when their raw values do not appear in evidence. It must not be treated as a confidentiality or authentication mechanism.
Hosts must therefore authenticate producers and restrict write authority. The profile guarantees deterministic interpretation of accepted evidence; it does not prove who produced a record or whether a malicious writer described a real comparison.
Knowledge writes
Reasoning writes hypotheses, not facts. The comparison is already an immutable observation, while surface and boundary interpretations remain Bayesian claims. This avoids presenting a time- or context-dependent visibility result as a permanent endpoint fact.
Installation writes only ontology definitions and rule definitions. Applying
the rule engine to a subject may write supported hypotheses, but it cannot
execute a request, choose an action, verify a result, or change a hypothesis to
Confirmed or Rejected.
Evidence intake
Hosts may construct a comparison directly or use the bounded
ApiVisibilityComparator described in API visibility evidence.
ingest_api_visibility_observation is the preferred host-facing write path: it
checks the expected resource, atomically stores the observation and relation,
then applies installed rules to the isolated comparison subject. Its typed
receipt distinguishes pre-commit rejection from post-commit reasoning failure.
The decision runner remains single-subject and is not used for paired ingress.
Usage
use termivar_scanner::{KnowledgeBase, RuleEngine, StandardApiReasoning};
let knowledge = KnowledgeBase::new();
let mut rules = RuleEngine::new();
StandardApiReasoning::new()?.install(&knowledge, &mut rules)?;
An authorized producer may then construct an ApiVisibilityComparison, call
to_observation(), and pass the bundle to
ingest_api_visibility_observation. That boundary atomically stores its
evidence and resource-scope relation before applying the rule engine to the
comparison subject. Supplying comparison inputs and deciding what subsequent
review is permitted remain host responsibilities.