Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bolt protocol

mushroomdb serve can listen for Bolt beside HTTP. The checked Python and JavaScript drivers connect without a configuration change; Go and .NET pass smaller best-effort checks. Java 6.3.0 rejects the server’s product identifier before queries and is currently unsupported. This page covers how to turn the listener on, how clients authenticate, TLS, the routing table, the version negotiation and its audit, value types, and what is not available over Bolt. For what a transaction guarantees, on Bolt and every other surface, see Transactions.

Turning the listener on

The listener is off unless you give it a port. 7687 is the port drivers use when a URI names none:

mushroomdb serve ./db --bolt-port 7687

or set MUSHROOMDB_BOLT_PORT=7687. It binds the IP of --addr (default 127.0.0.1) with that port, and prints a second line after the HTTP one:

listening on http://127.0.0.1:8080
listening on bolt://127.0.0.1:7687

--bolt-port 0 picks a free port. Any other --bolt-* flag without a port is a startup error.

In a container, bind all interfaces and name the host drivers should dial. Without --bolt-advertised-host a wildcard --addr is refused at startup:

docker run --rm -p 8080:8080 -p 7687:7687 -e MUSHROOMDB_TOKEN=changeme \
  ghcr.io/matthewsherlin/mushroomdb \
  serve /data --addr 0.0.0.0:8080 --bolt-port 7687 --bolt-advertised-host localhost
FlagVariableDefaultMeaning
--bolt-port <port>MUSHROOMDB_BOLT_PORTunset (off)Serve Bolt on --addr’s IP and this port
--bolt-tls, --bolt-plaintextMUSHROOMDB_BOLT_TLS=on|offTLS when --tls-cert is given, else plaintextSee TLS
--bolt-advertised-host <host>MUSHROOMDB_BOLT_ADVERTISED_HOSTthe bind IPThe host ROUTE hands drivers
--bolt-advertised-port <port>MUSHROOMDB_BOLT_ADVERTISED_PORTthe bound portThe port ROUTE hands drivers
--bolt-database <name>mushroomdbThe store’s name for ROUTE and database selection
--bolt-max-connections <n>MUSHROOMDB_BOLT_MAX_CONNECTIONS256Clients served at once
--bolt-tx-timeout-secs <secs>30Idle limit of an open transaction or result
--bookmark-timeout-secs <secs>5Longest a BEGIN or RUN waits for a bookmark

A flag wins over its variable.

Authentication

Bolt accepts exactly the credentials HTTP accepts, with the same effect:

  • the --token / MUSHROOMDB_TOKEN full-access token;
  • a --role-token / MUSHROOMDB_ROLE_TOKENS token, which runs as its role;
  • a key from keys.json with its tier: admin, write, or read (which cannot write over Bolt either). A SIGHUP reload reaches both listeners.

The driver sends the token as the password of basic authentication (the user name is ignored) or as a bearer token. With no token and no key configured, every client is let in, as on HTTP. A failed login is answered with one Unauthorized failure, the same for every cause, and the connection is closed.

The listener binds --addr’s IP, so the non-loopback rule is HTTP’s: a non-loopback --addr with no --token, MUSHROOMDB_TOKEN or admin key is refused at startup, before either listener starts.

Scoped access

A Bolt connection that logs in with a role token, or with a read key, sees and changes exactly what the same credential sees and changes through HTTP POST /query and /tx: the same rows, the same refusals with the same text, and the same kind of error. The role is applied to every statement on the connection; nothing about it is chosen by the client.

  • Reads. Every RUN reads through the role’s visibility: its keys, its labels with their visible_where test, intersected with its namespaces binding. See Masks. A node outside it does not appear, in a pattern, a count or an edge’s far end. MATCH (n) RETURN n for one role and for another, on the same store, return different nodes.
  • SHOW INDEXES and SHOW CONSTRAINTS list only the indexes and constraints on labels that a node the role can see carries, as they do over HTTP.
  • Auto-commit writes. A role with a write scope writes through the same scoped write path /query uses: a write outside the scope (a label it may not create, a namespace outside its binding) fails with Forbidden and the message HTTP gives with its 403, and a pattern naming a node the role cannot see matches nothing. A role with no write scope, and every read key, is refused every write (read-tier key: writes are not permitted for a key).
  • Schema statements. CREATE and DROP of an index or constraint fail with Forbidden for a role.
  • Explicit transactions. BEGIN resolves the role on the snapshot it freezes. Every statement reads through the role on that snapshot, so a commit that lands after BEGIN is not seen. A role token with a write scope writes inside a transaction exactly as it does in auto-commit: a write outside the scope, or targeting a node the role cannot see, fails with Forbidden and the same message HTTP gives. A role token with no write scope is refused every write (role-bound token: writes are not permitted). A read key is refused every write (read-tier key: writes are not permitted). COMMIT writes the buffered statements and returns their bookmark for a write-scope role (or the snapshot’s commit when nothing was written), and writes nothing for a no-scope role or read key. HTTP keeps its transaction open after a refused statement; on Bolt the failure ends the transaction, as the Bolt state machine requires, and the client sends RESET.
  • A role that no longer exists. The role is resolved for each statement and at each BEGIN, not only at HELLO. Once the store no longer defines it, the next statement or BEGIN fails with Unauthorized, as HTTP answers 401. A change to the role’s definition applies from the next auto-commit statement or BEGIN; an open transaction keeps the role as it was at its BEGIN.
  • Revoking a credential. A key removed from keys.json and reloaded with SIGHUP is refused on its next RUN, BEGIN, or COMMIT on any already-open Bolt connection, with Neo.ClientError.Security.Unauthorized; a revoked key’s open transaction is rolled back. HTTP refuses it on its next request. If the key’s tier or role is changed rather than removed, the behaviour depends on whether a transaction is open. Outside a transaction: the new identity takes effect silently on the next statement — a key demoted from write to read fails its next write with Forbidden. Inside a transaction: any change to the identity (tier or role) is refused immediately with Forbidden and the transaction is rolled back; the client can reset and start a new transaction under the updated identity. This rule covers RUN, BEGIN, and COMMIT: a COMMIT for a transaction whose key changed since BEGIN is also rolled back. A PULL or DISCARD that is already streaming its rows is not interrupted. See Access keys and the write tier.
  • No per-request narrowing. The /query body fields mask, namespace and as_of have no Bolt equivalent. Bolt applies only the credential’s own scope.

How HTTP statuses and Bolt failures correspond:

HTTPBolt failure
400, 409, 413a client error other than a security one (SyntaxError, Request.Invalid, ConstraintValidationFailed)
401Unauthorized
403Forbidden, with the same message
500a database error
503a transient error, which drivers retry

A full-access token, an admin key and a write key are unscoped on both protocols.

TLS

Bolt serves the same certificate as HTTP, from --tls-cert and --tls-key (a build with the tls feature; see Native TLS).

  • With --tls-cert and --tls-key given, Bolt is TLS by default and the second line reads listening on bolt+s://…. A plaintext Bolt listener beside TLS HTTP is started only when asked for, with --bolt-plaintext or MUSHROOMDB_BOLT_TLS=off.
  • Without them Bolt is plaintext, and --bolt-tls is a startup error.

Clients choose encryption from the URI scheme: use the +s form of the scheme (bolt+s://, or the routing scheme’s +s form) for a TLS listener, and the plain form otherwise. A plaintext client that connects to a TLS listener gets no Bolt reply and is closed.

The routing table

A driver given a routing URI asks ROUTE on connect and then talks only to the address it is told. This server is the whole cluster, so the table names one address for the WRITE, READ and ROUTE roles, valid for 300 seconds:

  • the host is --bolt-advertised-host, else the bind IP;
  • the port is --bolt-advertised-port, else the bound port. Set it when a container maps another host port onto the listener (-p 17687:7687).

A wildcard --addr (0.0.0.0 or ::) with no advertised host is refused at startup: a driver handed 0.0.0.0 would dial itself. The table carries addresses only; the scheme, and with it TLS, stays the one the client was given.

Database names: an absent, null or empty name, the drivers’ default name, and the --bolt-database name select the store, compared without regard to case. Any other name fails with DatabaseNotFound, in ROUTE, RUN and BEGIN alike.

Limits, deadlines and shutdown

  • Handshake deadline. A client has 10 seconds from connecting to finish the handshake (the TLS handshake gets 10 seconds of its own) and send a HELLO that authenticates, or it is closed.
  • Connection limit. Past --bolt-max-connections, a new client gets its handshake answered and then a transient DatabaseUnavailable failure to its first message, and is closed. Drivers retry a transient error. A client holds its slot from the moment it connects, before it authenticates, so clients that never log in can fill the limit for up to the handshake deadline (20 seconds with TLS).
  • Isolation. Each connection is its own task: a failure in one, a panic included, ends that connection only.
  • Shutdown. On SIGTERM the Bolt listener stops accepting at the start of the drain phase, with HTTP. Each connection is closed at its next idle point (between requests, or after the replies to the request it is running), and an open transaction is rolled back. Connections still open when the drain bound passes are cut off. The whole sequence stays inside --shutdown-timeout-secs; see Stopping cleanly.
  • Logging. A storage failure, a busy store or corruption reaches the client as a fixed message; its detail is logged on the server.

Versions the server speaks

Bolt versionStatus
5.0offered, preferred
4.4offered
5.1 and later (LOGON/LOGOFF)not offered

The server picks the first client slot, in the client’s order, that accepts one of its versions. It tries 5.0 before 4.4 within a slot.

Driver compatibility matrix

Audited 2026-10-20 from each driver’s handshake source on its default branch. “Latest version” is the newest release at that date.

DriverLatest versionMinimum offeredMaximum offeredSends 0x000001FFNegotiated with this server
Official Python driver6.4.13.05.8 in the opening handshake (6.1 only after a manifest exchange)yes, first slot5.0
Official JavaScript driver6.2.03.05.8 in the opening handshake (6.1 only after a manifest exchange)yes, first slot5.0
Official Java driver6.3.03.05.8 in the opening handshake (6.1 only after a manifest exchange)yes, first slot5.0; client refuses the HELLO product identifier
Official Go driver6.3.03.05.8 in the opening handshake (6.1 only after a manifest exchange)yes, first slot5.0
Official .NET driver6.4.03.05.8 in the opening handshake (6.1 only after a manifest exchange)yes, first slot5.0

All five send the same four slots, in this order:

SlotBytesMeaning
100 00 01 FFmanifest indicator
200 08 08 055.8 with range 8: versions 5.0 to 5.8
300 02 04 044.4 with range 2: versions 4.2 to 4.4
400 00 00 033.0

Why 5.0 and 4.4

Slots 2 and 3 of every checked driver’s handshake offer contain 5.0 and 4.4, so the version ranges overlap all five. Slot 1 is skipped and slot 2 holds 5.0; a client whose offer holds 4.4 and not 5.0 gets 4.4. Version negotiation alone does not prove compatibility: Java 6.3.0 rejects the truthful MushroomDB/<version> identifier in HELLO before queries. Python and JavaScript run the twelve-scenario suite in CI; Go and .NET pass three smaller best-effort checks, while Java reports a failed connection (see Driver compatibility tests). The handshake version ranges in the matrix come from the source audit. At a negotiated 5.0 the driver authenticates inside HELLO; LOGON and LOGOFF start at 5.1 and are not needed. Nothing in the audit requires a later 5.x message.

How the handshake is matched

  • A slot is [pad, range, minor, major]. It accepts the server version major.v for every v from minor - range to minor.
  • The manifest indicator 0x000001FF is skipped. The server does not run the manifest exchange; it answers with a plain version taken from the other slots, which every current driver accepts.
  • The reply is [0, 0, minor, major], or four zero bytes when no slot matches.
  • After the handshake, messages travel as chunks: a two-byte big-endian length, then that many bytes, ending with a zero-length chunk.

Driver compatibility tests

Every pull request and push to main runs the official Python driver 6.4.1 and the official JavaScript driver 6.2.0, pinned to those exact versions, against a release build of mushroomdb serve --bolt-port (the bolt-compat CI job, scripts in tests/bolt/). Each driver runs twelve scenarios, written the way an application uses the driver, and each checks the values it gets back:

#ScenarioWhat it checks
1Connect and authenticateThe token logs in; the server reports MushroomDB/<version> at Bolt 5.0
2Refused tokenA wrong token fails with Unauthorized
3Auto-commit readMATCH (n) RETURN count(n) returns an integer; a label count matches the data
4Auto-commit writeA CREATE (:Person {id: $id, …}) from parameters, a MERGE from parameters that matches and one that creates, read back by a later statement, with the drivers’ summary counters
5Transaction commitBEGIN, a write the transaction reads back, COMMIT; then a managed write transaction running MATCH (a {id: $a}), (b {id: $b}) CREATE (a)-[:K]->(b) and a managed read transaction that sees the edge
6Transaction rollbackA rolled-back write, and a managed transaction whose function throws, both leave nothing behind
7Streaming1000 rows of MATCH (n:Row) RETURN n pulled 100 at a time
8Value typesInteger, float, string, boolean, null, list, map, date, date-time with an offset, local date-time, duration and bytes come back as sent, as parameters and as stored properties
9RetryA managed write transaction whose commit conflicts with another client’s write gets the transient DeadlockDetected error, and the driver’s retry commits
10CALL db.labels()The full token sees every label; a role token sees only its role’s labels and nodes, and is refused a write
11Routing schemeThe driver asks ROUTE and runs its reads and writes on the advertised address
12BookmarksA read on a second session, given the first session’s bookmark, sees the first session’s commit

The separate bolt-drivers-best-effort job attempts three checks each for Java 6.3.0, Go 6.3.0 and .NET 6.4.0: connect and authenticate, read an integer count, and commit an explicit transaction whose write is read back. Go and .NET pass all three. Java passes none: its client rejects the server’s product identifier during connection, so no Java query or transaction is checked. Each language has its own step and summary result; Java’s failure remains visible there even when the job is green. These steps do not block a merge and do not cover the full twelve-scenario suite above.

No scenario depends on nodes or relationships inside maps or mixed lists arriving as structures.

Transactions and bookmarks

BEGIN, COMMIT and ROLLBACK are supported. A transaction reads a snapshot frozen at BEGIN, sees its own writes, and commits as one atomic batch; if another writer changed a node the transaction read or wrote, COMMIT fails with the transient DeadlockDetected and the driver retries from BEGIN. A commit a unique constraint refuses fails with ConstraintValidationFailed, a client error that is not retried: the same writes fail again until the clashing value changes. The isolation model, its limits (write skew included) and the rule that derived relationships appear only at commit are in Transactions; they are the same on every surface.

BEGIN fieldBehaviour
bookmarksBEGIN waits until the store has reached the highest bookmark, then freezes its snapshot. The wait is 5 seconds by default (--bookmark-timeout-secs); past it the request fails with a transient DatabaseUnavailable error.
dbChecked against the routing config, as ROUTE and RUN are: absent, null, empty, the driver default name or the configured database name (any case) are accepted; any other name fails with DatabaseNotFound.
mode"w" (default) or "r". A read transaction refuses write statements.
tx_timeout, tx_metadataChecked for shape and otherwise ignored. The connection’s idle limit (--bolt-tx-timeout-secs, 30 seconds by default) applies to every transaction.
imp_userNot supported; naming a user fails with Forbidden.

A bookmark is the text mushroomdb:commit:<n>, where n is a commit number. COMMIT and an auto-commit PULL return one; a transaction that wrote nothing returns the commit its snapshot read. Pass it in a later BEGIN or RUN to read at least that commit.

Every write statement that runs as an auto-commit RUN also runs inside BEGIN … COMMIT, with the same results and counts: MATCH (a {id: $a}), (b {id: $b}) CREATE (a)-[:K]->(b), MATCH … MERGE, UNWIND … <write> (such as UNWIND $rows AS r MATCH … CREATE …), the SET n = {…} / SET n += {…} map forms and SET / REMOVE … RETURN. So drivers’ managed write transactions (session.execute_write and the like), which send BEGIN, run them too. Schema statements (CREATE INDEX, CREATE CONSTRAINT and the rest) are still refused inside a transaction. See Write statements.

A COMMIT or ROLLBACK while a result is still open is a protocol violation, as the Bolt state machine specifies; pull or discard the open results first, as the drivers do. A role-bound token’s transaction reads through its role; a role with a write scope writes with the same scope restrictions as auto-commit, and a role with no write scope cannot write (see Scoped access). A write or admin key from keys.json writes in a transaction; a read key cannot run a writing statement anywhere. See Access keys and the write tier.

A transaction idle for --bolt-tx-timeout-secs (30 seconds by default) is rolled back, and the next request fails with TransactionTimedOut. An open result idle as long is closed. There is no lifetime limit on a Bolt transaction and no cap on open ones beyond --bolt-max-connections, since a connection holds at most one. Every open transaction is in the server’s memory: a restart rolls it back and nothing of it is written.

A Bolt null parameter is a null: WHERE $p IS NULL is true for it. (The HTTP route currently leaves a JSON null parameter out, so the same statement fails there with a missing-parameter error.)

Catalog procedures

RUN answers the procedure calls graph browsers and driver tools send to discover a store: CALL db.labels(), CALL db.relationshipTypes(), CALL db.propertyKeys(), CALL db.indexes(), CALL db.constraints(), CALL dbms.components() and CALL db.schema.visualization(), with the columns named in Procedures. dbms.components() reports the name mushroomdb, the engine version, and the edition open. A role-bound token sees only the labels, relationship types, property keys, indexes and constraints of the nodes its role can read, as it does for every other read. Calls other than these are an unknown-procedure error.

Value types

Results and RUN parameters use the same mapping in both directions. A value sent as a parameter, stored, and read back arrives as it was sent.

mushroomdb valueBolt valueNotes
null, boolean, integer, float, string, list, mapthe PackStream type of the same name
binaryBytesUp to 1 MiB as a stored property.
dateDate structure (0x44)Days since 1970-01-01.
local date-timeLocalDateTime structure (0x64)Seconds and nanoseconds.
date-time with a UTC offsetDateTime structure: 0x49 at Bolt 5.0, 0x46 at 4.45.0 counts seconds in UTC; 4.4 counts seconds in local time. A client must use the form its version uses; the other is refused. The offset is at most ±18:00.
durationDuration structure (0x45)Months, days, seconds and nanoseconds, kept exactly.

mushroomdb keeps date-times to the microsecond. A result is sent with nanoseconds = microseconds × 1000. A local date-time or date-time parameter with a nanosecond part that is not a whole number of microseconds is refused, not rounded. A parameter too large for the engine’s fields is refused too, for example a date past 32 bits of days, or a duration whose seconds overflow 64 bits of nanoseconds.

These parameters are refused with Neo.ClientError.Request.Invalid, and the message names the type:

Bolt valueWhy
DateTime with a named time zone (0x69, 0x66)Only a UTC offset is stored, not a zone name. Send the offset form.
Time (0x54), LocalTime (0x74)There is no time-of-day type.
Point2D (0x58), Point3D (0x59)There is no spatial type.
Node, Relationship, PathA graph entity is not a parameter value.
any other structure tagUnknown.

A temporal structure with the wrong number or type of fields fails the same way, as a malformed request. A refused parameter fails the RUN before the statement runs, so nothing is written; RESET makes the connection ready again.

Nodes, relationships and paths

A result column that is a bare node, relationship or path variable (RETURN n, RETURN r, RETURN p, and the same names through WITH, RETURN * or a grouping key) arrives as a graph structure, so record["n"]["name"], record["r"].type and a path’s nodes work in a driver:

ReturnedBolt structureFields at Bolt 5.0Fields at 4.4
nodeNode (0x4E)id, labels, properties, element_idid, labels, properties
relationshipRelationship (0x52)id, start_id, end_id, type, properties, element_id, start_element_id, end_element_idthe first five
pathPath (0x50)nodes, relationships (UnboundRelationship 0x72: id, type, properties, element_id), indicesthe same, without element ids
  • Labels. A node has at most one label, so labels is a list of one, or empty for a node created without a label.
  • Properties. Everything the node or relationship stores, as the statement saw it: inside a transaction its own writes, after a write its written state.
  • Element ids are derived from the element’s identity, so they are the same on every connection and after a restart: a node is n:<key> (n:ada); a relationship is r:<type>|<start key>|<end key> (r:KNOWS|ada|bob), with \ and | inside a part escaped by a backslash. The store holds at most one relationship of a type in each direction between two nodes, so these name it. Renaming a node gives it a new key and so a new element id.
  • Integer ids all fit in 53 bits, so a JavaScript number holds them exactly. A node’s id (and a relationship’s start_id / end_id) is the node’s dense id in the store: assigned when the node is created, never reused after a delete, and kept across a rename. A relationship’s id is a 53-bit hash of its element id, the same for the same relationship in every result; two relationships can collide (about one chance in two million among 65,000 relationships), so compare element ids when that matters. Node and relationship ids are separate namespaces.
  • id(n) in a query is the node’s key, not the Bolt integer id, so MATCH (n) WHERE id(n) = $node_id with a driver’s integer id matches nothing. Match on the key instead (n.id, or the element id without its n:).
  • Paths list each node and relationship once; indices walks them as Bolt defines, a negative relationship index meaning the hop goes against the relationship’s direction.
  • Scoped connections. A role-bound token gets only nodes its role can see, and a relationship only when both its ends are visible, as with every other read (see Scoped access).
  • Lists and maps. collect(n), collect(r), collect(p), a list of variables of one kind ([a, b]) and a path’s p.nodes and p.relationships arrive as lists of Node, Relationship or Path structures. A node or relationship inside a map built in the query ({who: n}), in a mixed list ([n, r], [n, 1]), or behind a function expression (head(collect(n)), coalesce(n, m), CASE … THEN n) also arrives as a Node or Relationship structure, not as its key or {type, start, end} map. elementId() accepts any expression that yields a node or relationship, including startNode(r), endNode(r) and nodes(p)[0].

HTTP, Python and the other surfaces render the same columns as they always have: a node as its key string, a path as a map, and a relationship as the map {type, start, end} (see Query language).

Write counters

The SUCCESS that closes a write statement carries a stats map, which drivers read as the result summary’s counters (nodes_created, contains_updates and so on):

KeyCounts
nodes-creatednodes created
nodes-deletednodes deleted
relationships-createdrelationships created
relationships-deletedrelationships deleted, including those a DETACH DELETE removes with their node
properties-setproperties set or removed, including those a CREATE or a MERGE writes with a new node or a new relationship
labels-addedone for each node created (a node is created with its label)
indexes-added, indexes-removedCREATE INDEX / DROP INDEX that changed the schema
constraints-added, constraints-removedCREATE CONSTRAINT / DROP CONSTRAINT that changed the schema

A schema statement’s summary has query type s, a write’s w and a read’s r. Only counters that moved are listed; a driver reads a missing one as 0. A statement that changed nothing (a read, a MERGE that matched with nothing to set, IF NOT EXISTS on an existing index) has no stats. Inside a transaction each statement’s SUCCESS carries that statement’s counters. A write without RETURN still returns its one row of counts (created, properties_set, deleted, relationships_created), as over HTTP; that row keeps its documented meaning, so its properties_set does not count the properties a CREATE writes with a node or a relationship.

EXPLAIN

EXPLAIN <statement> runs nothing. Its RUN reply has no fields, a PULL returns no records, and the closing SUCCESS carries the plan as plan, where drivers read summary.plan. The plan is a map with operatorType, args, identifiers and children; the last operator of the statement is the root and each operator’s child is the one before it (a UNION is a Union root with one child per part). args holds the operator’s fields, including the index a lookup reads (index, null when it scans). The summary’s query type is r, a transaction may run it, and a role token may run it, naming only the indexes it could list. See EXPLAIN for the operators; the plan is a diagnostic and its shape may change between releases. PROFILE is refused.

RESET while a request is running

A RESET sent while an earlier request is still running takes effect at the first of these points:

  • a bookmark wait ends within 10 ms, and that request is answered IGNORED;
  • the next request queued in the same batch is IGNORED;
  • otherwise when the running statement or commit returns. A statement inside the engine cannot be interrupted: it finishes and its reply is sent, and a COMMIT or auto-commit write that was already submitted stays committed.

The RESET is answered SUCCESS after every earlier request has been answered, and the connection is then ready; an open transaction is rolled back. A client that disconnects during a request has its transaction rolled back.

Not available over Bolt

Bolt carries Cypher statements and the catalog procedures above. These are HTTP or embedded only:

  • Time travel. There is no as_of on a Bolt statement; every statement reads the current store (or the transaction’s snapshot). Use HTTP POST /query with as_of, or the embedded open_at; see Time travel.
  • Per-request narrowing. mask and namespace have no Bolt form; a connection reads with its credential’s scope only (see Scoped access).
  • Operations with no Cypher statement: backups, rules (POST /rules), rule suggestion, explain of an association, node and relationship history, the algorithm routes, live streams (/watch, /subscribe) and the service routes (/health, /ready, /stats, /metrics). See the HTTP API.
  • Writes in a role-scoped transaction with no write scope. A role token with no write scope, and every read key, cannot write inside a transaction. A role token with a write scope writes with the same scope restrictions as auto-commit RUN.
  • Schema statements inside a transaction, PROFILE, impersonation (imp_user), Bolt 5.1 and later (LOGON / LOGOFF), and the Time, LocalTime, Point and named-time-zone value types.