Resolved driver behaviour for a single connection.
Resolved from the negotiated Bolt version (and optionally the HELLO response metadata) at connection time, then stashed on the connection state and threaded into every pack/unpack/message call. Code pattern-matches on policy fields and never reads a Bolt or server version directly.
Policy is an internal distillation of negotiated facts, not a user-facing
configuration surface. Users influence policy by passing connection options
(e.g. constraining :versions at negotiation).
Summary
Types
Named Cypher features the server understands, beyond what the coarse
cypher_5/cypher_25 language selectors imply.
DateTime encoding dialect. Bolty only ever negotiates Bolt 5.0+, which always
uses the evolved struct tags (0x49/0x69) — :evolved is the only value.
HELLO wire field name for disabled notification categories/classifications.
Policy fields a caller may assert by hand via the :capabilities connection
option — the server-capability flags only.
Functions
Applies caller-asserted capability overrides over a resolved policy.
Types
@type cypher_feature() :: :disjoint_by | :vector_search_in_predicate | :vector_hfq
Named Cypher features the server understands, beyond what the coarse
cypher_5/cypher_25 language selectors imply.
A curated set, not an exhaustive one: Cypher gains features every calendar
release as it converges on GQL, and a boolean struct field per feature would
neither scale nor be worth the permanent API surface. Membership is the check
(:disjoint_by in policy.cypher_features), so naming a new feature is one
atom here plus one row in the docs.
Every member is a Cypher 25 feature: it errors under a CYPHER 5 prefix
even on a server that lists it, so gate on cypher_25 as well when emitting
an explicit language selector.
:disjoint_by—DISJOINT BY (expr, …) | AUTO | NONEbatch scheduling onCALL { … } IN CONCURRENT TRANSACTIONS, which prevents lock contention in parallel writes (Neo4j ≥ 2026.06).:vector_search_in_predicate—INpredicates in vector search filters (SEARCH n IN (VECTOR INDEX … WHERE n.prop IN […] LIMIT …)), Neo4j ≥ 2026.06. Equality filters predate this; only theINform is new, and on 2026.05 it fails with an internal server error rather than cleanly, so gating matters.:vector_hfq— Hi-Fidelity Quantized vector search: thevector.quantization.typeandvector.default_search_expansion_factorindex options onCREATE VECTOR INDEX(Neo4j ≥ 2026.06). A preview feature in 2026.06, expected GA in the next release — the availability boundary holds either way, but the option semantics may still move.
@type datetime() :: :evolved
DateTime encoding dialect. Bolty only ever negotiates Bolt 5.0+, which always
uses the evolved struct tags (0x49/0x69) — :evolved is the only value.
@type notifications_field() ::
:notifications_disabled_categories | :notifications_disabled_classifications
HELLO wire field name for disabled notification categories/classifications.
:notifications_disabled_categories— Bolt ≤ 5.5:notifications_disabled_classifications— Bolt 5.6+ (spec rename)
@type overridable() :: :cypher_5 | :cypher_25 | :dynamic_labels | :cypher_features
Policy fields a caller may assert by hand via the :capabilities connection
option — the server-capability flags only.
The wire-level dimensions (datetime, notifications_field, gql_errors,
vectors) are negotiated facts about the Bolt connection, not opinions, and
overriding one would just corrupt the wire; they are rejected.
@type t() :: %Bolty.Policy{ cypher_25: boolean(), cypher_5: boolean(), cypher_features: MapSet.t(cypher_feature()), datetime: datetime(), dynamic_labels: boolean(), gql_errors: boolean(), notifications_field: notifications_field(), vectors: boolean() }
Functions
@spec override( t(), keyword() ) :: {:ok, t()} | {:error, Bolty.Error.t()}
Applies caller-asserted capability overrides over a resolved policy.
Bolty infers Cypher capabilities from the HELLO server string, which only
works for a server that both is Neo4j and reports its real calendar
release. A Bolt server that emulates Cypher 25 without following Neo4j's
calendar — or that wears a pinned Neo4j agent string while implementing a
different subset — cannot be inferred correctly in either direction, so the
caller gets to state the truth instead:
Bolty.start_link(capabilities: [cypher_25: true, cypher_features: [:disjoint_by]])Each given value replaces the inferred one rather than merging, so an override can withdraw a wrongly-inferred capability as well as add a missing one. Anything not named keeps its inferred value.
Returns {:error, %Bolty.Error{}} for an unknown or non-overridable field, so
a typo surfaces at connect rather than silently doing nothing.