All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.4.0] - 2026-07-07
Added
Arcadic.Schema— tenant-blind schema introspection:types/1,properties/2,indexes/2(with a:typefilter),buckets/1, anddatabase/1(the engine config,schema:database) (all +!). SQL-onlySELECT FROM schema:*; a caller type name binds as a$paramand isIdentifier-shape-guarded (value-free); ArcadeDB's@propsserializer noise is deep-stripped at every nesting depth.Arcadic.Import—database/3(+!) wrappingIMPORT DATABASE. The source URL is validated against a positive character allowlist (closing the interpolated-URL injection surface, since the URL cannot be a bound parameter and ArcadeDB honours backslash-escapes inside string literals) and a scheme allowlist (http/https/file);with:accepts number, boolean, and charset-allowlisted string settings, emitted as ArcadeDB's no-parensWITH k = vgrammar. Import errors are reflected faithfully — a private/loopback host trips ArcadeDB's SSRF guard (:unauthorized/java.lang.SecurityException, distinct from an auth failure'sServerSecurityExceptionviaerror.exception).Arcadic.Export—database/3(+!) wrappingEXPORT DATABASE file://<name>, symmetric toArcadic.Import: the bare export name is path-traversal-guarded (value-free), andwith:reuses the same number/boolean/string settings grammar.- HTTP
query_streampages WHERE-less SQL by an O(n)@ridkeyset cursor (offset fallback for WHERE'd statements) and supports Cypher streaming via a callerorder_key: "id(v)"(offset,$nameplaceholders). The comment guard is language-aware (//rejected for Cypher). Insidetransaction/3over Bolt, streaming uses the O(n) in-transaction cursor. - Transaction-scoped Bolt streaming —
query_stream/4insidetransaction/3streams over the transaction's own connection (sees uncommitted writes), guarded so anexecutecannot interleave an open cursor on the shared socket; the cursor callbacks disconnect on a wire fault (desync-safe). Consume the stream inside thetransaction/3body (it is bound to the tx connection). - Bolt over TLS —
scheme: "bolt+s"withssl_opts.bolt+sis secure by default (verify_peeragainst the OS trust store, via boltx's invertedbolt+sscscheme under the hood);ssl_opts: [verify: :verify_none]is an explicit caller opt-in to skip verification. A:uriopt is rejected (it would bypass the scheme translation and silently skip verification). - Bolt now fails loud if a
BOLT_USER/BOLT_PWD/BOLT_HOST/BOLT_TCP_PORTenvironment variable is set — both at pool setup (start_link/1/setup/1) and again on every connect/reconnect, because boltx re-reads them with precedence over arcadic's explicit config at connect time (so a var set after startup is caught too); unset the var.
Changed
- Bolt RUN/PULL wire-framing is deduplicated to a single site (
stream_run/stream_pull), shared by the non-transaction stream and the in-transaction cursor callbacks.
Fixed
Arcadic.query/4,command/4, andquery_stream/4now reject a non-keyword-listoptswith a value-freeArgumentError("opts must be a keyword list"). Previously an improper-listopts(e.g.[:foo]) raised aKeyworderror whose message echoed the offending entry — a Rule-3 value leak on the core query paths. The opt-key guard is now shared across the query,Schema,Import, andVectorsurfaces.
Notes
- Documented an upstream ArcadeDB server hazard for operators running Bolt over TLS: a single untrusted-cert TLS handshake failure can wedge ArcadeDB's shared Bolt listener (~100% CPU, no ServerHello for any client) until the server restarts. arcadic's client-side TLS is unaffected. Tracked upstream at ArcadeData/arcadedb#5106.
[0.3.0] - 2026-07-05
Added
Arcadic.Vectorsparse + hybrid completion:create_sparse_index/5(+!),drop_sparse_index/4(+!),sparse_neighbors/8(+!) over ArcadeDBLSM_SPARSE_VECTORindexes ((tokens, weights)pair; rows ranked by top-levelscore).create_sparse_indexopts:dimensions,modifier(:none|:idf).filter(param-bound candidate RID set),group_by, andgroup_sizeopts onneighbors/6,sparse_neighbors/8, andfuse/3.- A
[:arcadic, :vector, :sparse_index_preexisting]telemetry event when a sparse index is created over rows that already exist (which a sparse index does not retro-index).
[0.2.1] - 2026-07-05
Fixed
- Install instructions in the README and getting-started notebook pointed at a pre-publish
path dependency and
~> 0.1; corrected to{:arcadic, "~> 0.2"}from Hex.
Added
- README: Hex.pm + hexdocs badges, a Benchmarks section (linking the
bench/harness and the 100k result set), and a Bulk-loading note;usage-rules.mdbulk-loading entry (IMPORT DATABASE/transaction/3).
No library code changed in this release — docs/packaging only.
[0.2.0] - 2026-07-05
Added
Arcadic.Vector— dense vector search over ArcadeDBLSM_VECTORindexes:create_dense_index/5(+!),drop_dense_index/3(+!),neighbors/6(+!),fuse/3(+!), andindex_ref/2. Tenant-blind; the query vector,k,ef_search, andmax_distancebind as params; index refs are identifier-validated; metadata keys/values and query/fusion option inputs are allowlisted and validated value-free (similarity,encoding,quantization,fusionagainst their ArcadeDB enums).neighbors/6rows carry adistancewhose scale depends on the indexsimilarity(COSINE0..1ascending; DOT_PRODUCT negative);fuse/3rows are ranked byscore. Sparse retrieval and the Ash-native surface are named non-goals.
[0.1.0] - 2026-07-04
Added
Arcadic.Conn— pure-data connection handle (redactingInspect) andArcadic.connect/3/Arcadic.with_database/2to build and derive handles.Arcadic.query/4+query!/4(idempotent read endpoint) andArcadic.command/4+command!/4(write endpoint), params-only ($name), Cypher-default with SQL/Gremlin/GraphQL/Mongo/SQLScript opt-in.Arcadic.command_async/4— fire-and-forget write (server enqueues, returns:okon HTTP 202).- Session transactions:
Arcadic.transaction/3(commit on return, reraise on exception) andArcadic.rollback/2(intentional abort →{:error, reason}). Arcadic.Server— server admin:create_database/2(+!),drop_database/2(+!),database_exists?/2,list_databases/1,ready?/1.Arcadic.Error/Arcadic.TransportError— typed error taxonomy with boundary redaction (quarantineddetail, value-free reasons).Arcadic.Transport— the transport behaviour seam, with the defaultArcadic.Transport.HTTP(Req/Finch) implementation.Arcadic.Telemetry— value-free:telemetry.span/3spans (no statement, params, values, or database name).Arcadic.Identifier— allowlist identifier validation.- Migration runner:
Arcadic.Migration(behaviour),Arcadic.MigrationRegistry(use+migrations [...]), andArcadic.Migrator(migrate/2,status/2,rollback/3,reset/2,pending_migrations/2), tracking applied versions in_arcadic_migrations. Arcadic.Transport.Bolt— optional Bolt transport (Bolt v4, non-TLS scheme) via the optionalboltxdependency; server admin remains HTTP-only.Arcadic.query_stream/4— Bolt-only lazyStream.t()of raw row maps, chunked over BoltPULL/has_more(defaultchunk_size: 1000); a:timeoutopt bounds each RUN/PULL receive (default:infinity), raising%Arcadic.TransportError{reason: :timeout}on breach; guarded off HTTP and inside transactions with a typed:not_supported.Arcadic.Transport.Bolt.setup/1— single-sourcetransport_optionsbuilder ([bolt: pool, bolt_opts: opts]).Arcadic.Telemetry.event/3— allowlist-validated manual telemetry for lazy ops;[:arcadic, :query_stream, :start | :stop]events (value-free).
Fixed
Arcadic.Transport.Boltnow threadsconn.databaseinto every Bolt RUN/BEGIN, sowith_database/2selects the database on Bolt (was hitting the connection default).- Bolt
transaction/3maps a commit-failure to a typed%Arcadic.Error{reason: :transaction_error}instead of leaking DBConnection's bare:rollbackatom. Arcadic.Transport.Bolt— a failed Bolt connect (wrong password, or a Bolt conn pointed at a non-Bolt port) no longer leaks a:gen_tcpsocket. arcadic now owns the connect handshake and HELLO on both the per-stream connection and the DBConnection pool, closing the socket on every failure; a bad-password stream connect surfaces:unauthorized, and the connect HELLO is bounded byconnect_timeout. Connect-time errors are redacted on both sites: a HELLO response arcadic's parser cannot classify returns a value-free:bolt_protocol_errorinstead of a raw exception carrying server bytes, and the DBConnection pool's connect error drops the server-supplied failure message (keeping the error code/class) so it cannot ride a connect-failure log line.