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.

[1.1.0] - 2026-08-23

Added

  • request_timeout — the whole-response bound. New conn field/connect opt (Arcadic.connect/3, request_timeout: ms), per-call override on Arcadic.query/command (request_timeout:), and transport wiring on every HTTP request site (post/4, raw_get/3, batch_ingest, ts_write). It maps to Finch's request_timeout, bounding the COMPLETE response — the hazard class receive_timeout (max wait per received chunk) cannot bound: a peer that trickles bytes can otherwise hold a call open indefinitely. Default nil keeps Finch's own default (:infinity) — existing conns are unchanged. Proven on a real socket: a 20-byte trickle at 200 ms intervals under timeout: 60_000 is killed at ~400 ms by request_timeout: 400 (TransportError{reason: :timeout}); the same trickle with no request_timeout completes. Mutation-red-proven (dropping the wiring reds exactly the kill test).

Fixed

  • A 401 response (invalid/expired bearer token — the body carries only error/requestId, no exception FQN; live-probed on 26.8.1) now maps to :unauthorized instead of the generic :server_error — a credential failure read as a server fault hides the re-auth remedy.
  • Arcadic.connect/3 now REJECTS a base_url carrying userinfo together with an explicit :auth (value-free ArgumentError). At the HTTP layer the URL credentials silently override the Authorization header, so a caller deliberately overriding credentials via :auth would send the URL's instead.

Changed

  • req floor bumped ~> 0.5~> 0.7 (0.7 is where request_timeout exists as a request option; resolved 0.7.3, full suite green on it).

[1.0.0] - 2026-08-22

The first stable release: the full client surface (HTTP, Bolt, gRPC) is feature-complete, every dependency-security advisory is closed, and the entire 139-test integration surface is live-proven (see Verified below). 1.0.0 is a compatibility commitment for the public API.

Historical backfill

Surface that shipped in earlier releases without a changelog entry, recorded here for the 1.0 record:

  • The :retries command option (server-side optimistic-lock retry for concurrent autocommit writes) has existed since v0.1.0 (http.ex — forwarded to ArcadeDB's retries body param). It was first DOCUMENTED in the 0.7.1-era reliability work with a live tripwire (100 concurrent writers, ~85 conflicts without → 0 with retries: 10).
  • Arcadic.Transport.execute_with_index/4 — the offset-paging read underlying HTTP result streaming.
  • Arcadic.DDLBody (internal) — the reject-not-escape guard for ArcadeDB DDL body literals (DEFINE FUNCTION / CREATE TRIGGER), and Arcadic.Admin (internal) — the shared, value-free admin span + transport delegation plumbing. Both @moduledoc false by design; named here so the security-relevant guard is on the record.
  • notebooks/operations.livemd — the change-events / functions / triggers / materialized-views / geo notebook (the fourth Livebook, alongside getting_started, graphrag, and timeseries).

Verified

  • Live-verification record (2026-08-22, arcadedata/arcadedb:latest = 26.9.1-SNAPSHOT, throwaway containers on this host): the ENTIRE 139-test integration surface — every test excluded from the default unit pass — ran green in one session on the post-migration tree: HTTP :integration 55 (incl. the server-side :retries tripwire), websocket 6, time-series 10, :integration_grpc 38 (grpc 1.0.4 + mint adapter), :integration_grpc_tls 5, :integration_shutdown 1, and the Bolt family — :integration_bolt 22 (bolt, bolt_explain, bolt_stream, tx-scoped B6 streaming) + :integration_bolt_tls 2 (verify_peer connects with the trusted CA; fails closed untrusted). Bolt substrate recipes (plugin flag, both env families, the arcadedb.ssl.* keystore spellings) are recorded in bolt_test.exs's moduledoc; TLS material for the grpc and Bolt suites comes from test/support/tls/gen-grpc-certs.sh (now also emitting the server-side PKCS12 keystore + JKS truststore).

Added

  • gRPC TLS trust-store selection + a live fail-closed suite. grpcs:// still hardcodes verify: :verify_peer (never verify_none), but callers may now select a private CA via transport_options: [cacertfile: path] (or cacerts: as a DER list) — an allowlist merge, so a caller chooses the trust store but cannot downgrade verification itself. The trust selection is part of the ChannelPool key ({host, port, tls?, trust}), so pooled channels never cross trust stores (a cross-vendor review finding: with the old key, a channel established under one trust anchor was silently reused by conns with a different trust config — fail-closed would have become first-conn-wins through the pool); passing both cacertfile and cacerts is rejected value-free instead of letting OTP precedence silently pick one. New :integration_grpc_tls live suite closes the long-standing "no live TLS coverage" gap: cert recipe (test/support/tls/gen-grpc-certs.sh) + a TLS-enabled gRPC ArcadeDB (recipe in the test moduledoc; on ARM64 images add -Dio.grpc.netty.shaded.io.netty.handler.ssl.noOpenSsl=true to dodge netty-tcnative's LSE-atomics crash). Proven live: trusted CA connects and executes; an untrusted CA and the bare OS store both fail the handshake — pooled and unpooled, each with its own liveness anchor — and non-vacuous: a scratch verify_none mutation reddens both fail-closed tests, and the pool-crossing test was observed RED before the key fix.

Fixed

  • Restore the mix format --check-formatted gate: the vendored generated protobuf module (lib/arcadic/transport/grpc/proto/) is excluded from formatter inputs, and the live :retries tripwire integration test — committed unformatted — is reformatted.
  • Restore the mix compile --warnings-as-errors gate: drop the unreachable {:error, {:already_started, _}} clause in the gRPC client-supervisor bootstrap (Elixir 1.20 type analysis proves it dead; the catch-all already evaluated to the same :ok, so behavior is unchanged).

Security

  • Dependency lock refresh clears every advisory with a released fix: mint 1.9.3, hpax 1.0.4, bandit 1.12.5, plug 1.20.3, cowboy 2.18.0, cowlib 2.19.0, gun 2.5.0 (plus plug_crypto/ranch rides). The composite mix audit gate dropped from 21 advisories to 8; the remainder (grpc 0.11.5's CRITICAL + 3 HIGH, plus gun/cowlib advisories unpatched upstream) is fully resolved by the grpc 1.x migration below.
  • grpc 1.x migration (~> 0.11~> 1.0, locked 1.0.4): closes every grpc advisory — CRITICAL CVE-2026-48853 (RCE via erlpack deserialization) plus HIGHs CVE-2026-48599/53430/48854 — all fixed in grpc 1.0.0. The channel pins the mint adapter explicitly (grpc 1.x defaults to gun): mint 1.9.3 is patched and already a runtime dependency via req, and the move drops gun/cowboy/cowlib from the dependency tree entirely — their advisories (unpatched upstream at the latest published versions) leave the tree with them. mix audit is now fully green: zero advisories, zero retired packages. Internal to the migration: the 0.11 lazy client-supervisor bootstrap is gone (grpc 1.x auto-starts its own connection supervisor), and ChannelPool now best-effort-disconnects a stale cached channel when reconnecting replaces it (cross-vendor review finding: grpc 1.x owns connection processes under its own supervisor with random names, so dropping the handle without disconnecting would orphan that connection tree); pool teardown is best-effort for the same reason. Verified live against ArcadeDB 26.9.1-SNAPSHOT (gRPC suite 38/38; HTTP 55, websocket 6, time-series 10, shutdown 1 on the refreshed lock). Consumers pinning grpc 0.11.x will see a resolver conflict — intended; re-pin to ~> 1.0.

Changed

  • ArcadeDB 26.9.x substrate drift, pinned in the live suites (characterization re-pins, verified against 26.9.1-SNAPSHOT): a mixed line-protocol write with an unknown timeseries type still partially writes (the known line lands) but now answers HTTP 400 with a structured partial-write body instead of the 26.7.2 silent 200 — arcadic maps it value-free to {:error, %Arcadic.Error{reason: :server_error}}; and Server.shutdown/1 halts asynchronously (graceful drain can outlive the ack), so the live suite polls health with a bounded deadline.
  • Align the contributor guide with the released 0.7.1 surface and the post-release server-side retries: documentation and live tripwire already present on main.

[0.7.1] - 2026-07-14

Changed

  • Docs: the README and usage-rules.md now document the gRPC transport (Arcadic.Transport.Grpc) alongside HTTP and Bolt — the 0.7.0 release shipped the transport but the README/hexdocs still described only HTTP + Bolt. No code change (maintenance release for documentation currency).

[0.7.0] - 2026-07-14

Added

  • Arcadic.Server.database_info/1 — database-level info (%{database, type, records, classes, size_bytes}) for the connection's database. Over gRPC it maps to GetDatabaseInfo (all fields); over HTTP to SELECT FROM schema:database (name/size; record and class counts are nil, not cheaply available on that transport).

  • Arcadic.Transport.Grpc — an optional third transport over ArcadeDB's gRPC plugin, behind the new optional deps {:grpc, "~> 0.11"} and {:protobuf, "~> 0.17"}. Its headline is streaming: StreamQuery in CURSOR mode is a real server cursor (O(n), server-paced, language-agnostic), where HTTP result streaming offset-pages (O(n²) in the general case) and Bolt streams Cypher only. The transport implements the full surface it can support:

    • Reads/writesexecute/4 (ExecuteQuery/ExecuteCommand) and query_stream/4 (the CURSOR win).
    • Transactionsbegin/commit/rollback, so Arcadic.transaction/3 and tx-scoped reads/writes work over gRPC.
    • Bulk graph ingestArcadic.Bulk.ingest/3 (via GraphBatchLoad), the gRPC twin of the HTTP /batch endpoint (same counts/id_mapping result; transport-transparent).
    • Arcadic.Ingest — document bulk-insert into a class (BulkInsert, or InsertStream via :chunk_size), returning insert-summary counts.
    • Arcadic.Record — single-record create/lookup/update/delete by @rid (raw maps).
    • AdminArcadic.Server database management (list_databases/database_exists?/create_database/ drop_database/info/database_info) and Arcadic.explain/profile.
    • Arcadic.Transport.Grpc.ChannelPool — an opt-in, caller-supervised shared-channel cache; add it to your supervision tree for channel reuse (absent, a fresh channel per call is used).

    Errors are value-free (an atom reason, never the gRPC wire message; per-row ingest errors surface only a row index + a categorical code). Transport is plaintext by default; enabling TLS (a secure grpcs:// scheme or transport_options: [tls: true]) verifies the server certificate against the OS trust store (verify_peer, never verify_none). A few operations are intentionally HTTP-only: server settings, user management (the server itself does not implement it over gRPC), token login/logout, time-series, and HA read-consistency — use an HTTP Conn for those. Select it with transport: Arcadic.Transport.Grpc and a grpc://host:port URL; credentials come from Conn.auth. HTTP/Bolt-only consumers are unaffected: the transport and its generated protobuf stubs are compile-guarded on the optional deps, so a consumer that doesn't add :grpc/:protobuf compiles and ships without them.

[0.6.0] - 2026-07-14

Added

  • Arcadic.transaction/3 accepts an opt-in :retry option (true for defaults, max_attempts: 3, base_backoff_ms: 50, max_backoff_ms: 1000, or a keyword overriding any of those). Off by default (unchanged behavior). On a transient server fault (:concurrent_modification, :not_leader, and a pre-commit :timeout) it retries with jittered exponential backoff up to :max_attempts. The retried function must be idempotent - it may run more than once. Emits [:arcadic, :transaction, :retry] telemetry per attempt (%{attempt} measurement, %{reason} metadata).
  • Arcadic.Conn.with_consistency/2 and connect(consistency: ...): a read-consistency level for subsequent reads: :eventual (default, sends no extra header), :read_your_writes, or :linearizable. HTTP-only; a non-default level on a Bolt connection raises. On a single-server deployment, :read_your_writes is a harmless no-op.
  • Arcadic.query_bookmarked/4 / command_bookmarked/4 (+ ! variants), like query/4/command/4 but return {:ok, rows, conn'}, where conn' carries a monotonically-advancing read-your-writes bookmark. Thread conn' into the next call to observe your own writes. HTTP-only; bookmarked calls target the primary host and do not participate in multi-host failover.
  • connect(hosts: [...]): additional base URLs for multi-host availability failover. Reads fail over to the next host on any connection error; writes fail over only on a pre-send connect error, never on an ambiguous post-send close. A session (inside transaction/3) pins to whichever host answers first. This is availability failover, not load balancing; front a cluster with a load balancer if you want request distribution.
  • New Arcadic.Error reason :not_leader: the target node is not the cluster leader and could not forward the write (ServerIsNotTheLeaderException). Treated as retriable by managed retry and as failover-eligible by multi-host failover (the write was rejected, nothing applied).
  • Arcadic.Changes — a live change-events client for ArcadeDB's /ws feed. Start it under your own supervision tree (start_link/1) and subscribe/3 a database to receive {:arcadic_change, %Arcadic.Changes.Event{}} messages. Best-effort at-most-once: the feed has no replay, so a reconnect delivers a :reconnected marker (events during the gap are lost) and a full delivery buffer delivers one :overflow marker after dropping the oldest events — either marker means the subscriber should reconcile. A terminal 401/403 handshake failure delivers {:arcadic_change_error, :unauthorized} (then stops, no reconnect spin); a server-rejected subscribe delivers a non-terminal {:arcadic_change_error, :subscribe_rejected}. Requires the optional mint_web_socket dependency; start_link/1 returns {:error, :mint_web_socket_not_available} without it, and validates the :conn value-free (:invalid_auth / :invalid_url_scheme / :invalid_max_buffer — an unrecognized URL scheme is refused, never downgraded to plaintext).
  • Arcadic.Functiondefine/4 / delete/2 (+ !) for ArcadeDB user-defined functions (DEFINE FUNCTION / DELETE FUNCTION). A function name is a dotted library.fn; the body is a single-line, single-quoted literal (ArcadeDB's DDL string literal has no escape). define/4 takes a single trailing opts keyword list — :params (a list of parameter names) and :language (:js default, :sql, :cypher) — matching the other DDL helpers. Call a defined function from an ordinary query/4/command/4 via the backtick idiom.
  • Arcadic.Triggercreate/4 / drop/2 (+ !) for ArcadeDB triggers (CREATE TRIGGER / DROP TRIGGER), firing a :timing (:before/:after) × :event (:create/:delete/:update/:read) action in :sql, :javascript, or :java. Shares Arcadic.Function's single-line/single-quoted body limit.
  • Arcadic.MaterializedViewcreate/3 / drop/2 (+ !) for ArcadeDB materialized views (CREATE MATERIALIZED VIEW / DROP MATERIALIZED VIEW) from a raw SELECT statement.
  • Arcadic.Geocreate_index/4 / drop_index/3 (+ !) for a GEOSPATIAL index over a string property holding WKT (this ArcadeDB build has no native POINT schema type). Geospatial querying rides ordinary query/4/command/4.
  • Arcadic.TimeSeries — a client for ArcadeDB's time-series wire family. Requires ArcadeDB ≥ 26.7.2 (an older server 404s every /api/v1/ts route).
    • create_type/4 / drop_type/2 (+ !) — TIMESERIES DDL (fields, tags, precision, shards, retention, compaction interval); add_downsampling/3 / drop_downsampling/2 (+ !) — downsampling policies.
    • create_aggregate/3 / refresh_aggregate/2 / drop_aggregate/2 (+ !) — continuous aggregates (CREATE/REFRESH/DROP CONTINUOUS AGGREGATE), materializing a raw SELECT as a document type.
    • write/3 / write_lines/3 (+ !) — Influx line-protocol writes: write/3 builds the wire format from structured point maps (typed fields, string tags, Identifier-validated names); write_lines/3 is a raw passthrough for an already-built line-protocol batch. Append-only, non-idempotent — see the module's Operational contract before relying on retries or mixed-type batches.
    • query/3 / latest/3 (+ !) — JSON reads: query/3 returns the raw columnar shape or, with :aggregation + :bucket_interval, bucketed aggregations; latest/3 returns the newest point matching at most one tag.
    • prom_query/3, prom_query_range/6, prom_labels/2, prom_label_values/3, prom_series/3 (+ !) — the PromQL read family (instant, range, labels, label values, series matching), decoding the Prometheus data envelope.
    • Four new optional Arcadic.Transport callbacks (ts_write/3, ts_query/3, ts_latest/3, ts_prom_get/4) back the wire family, HTTP-only (Bolt returns {:error, %Arcadic.Error{reason: :not_supported}}).
    • New notebooks/timeseries.livemd — DDL, writes, query/latest, PromQL, continuous aggregates, downsampling, and pointing Prometheus/Grafana at ArcadeDB.

Changed

  • connect/3 and transaction/3 now reject unknown option keys value-free (an ArgumentError naming the allowed set), matching query/command. A typo'd option — consistancy:, or retires: for retry: — was previously ignored silently, which for a connection-control option meant the wrong default applied (a mis-spelled retry: silently meant no retry). The offending value is never echoed.

[0.5.0] - 2026-07-10

Added

  • Arcadic.explain/4 + Arcadic.profile/4 (+ !) — surface the ArcadeDB EXPLAIN/PROFILE plan (%{plan, plan_tree, rows}) that query/command silently dropped. explain is plan-only; profile executes the statement (a write mutates). Works over HTTP and Bolt (Cypher-only).
  • query/command/query_stream now return {:error, %Arcadic.Error{reason: :use_explain}} on a bare EXPLAIN/PROFILE (was a silent {:ok, []}).
  • Arcadic.Server — server administration, expanded beyond database lifecycle: info/2 (server info/metrics, mode: :basic | :default | :cluster), metrics/1, health?/1, events/1, set_server_setting/3 / set_database_setting/3 (key + value both allowlist-validated value-free), open_database/2 / close_database/2, align_database/2 (cluster-only — a single-server node returns a server error), check_database/2 (fix: true runs CHECK DATABASE FIX, returns the integrity map), profiler/2, and shutdown/1 (a successful shutdown typically surfaces as a transport-closed error, since the server stops responding mid-request), alongside the existing create_database/2 / drop_database/2 / database_exists?/2 / list_databases/1 / ready?/1. HTTP-only, tenant-blind.
  • Arcadic.Security — server security & auth admin (HTTP-only): login/1 / logout/1 (session tokens), sessions/1, users/1, groups/1, api_tokens/1, create_user/2, and drop_user/2 (all + !). create_user/2's password is JSON-encoded into the server command and never echoed — an unencodable spec (e.g. a non-UTF-8 password) is rejected value-free as {:error, :invalid_user_spec} before any wire call.
  • Arcadic.Backup — backup and restore (HTTP-only): backup/2 (BACKUP DATABASE, optional :to target URL), list/1, and restore/3 (all + !). A :to target and the restore URL are allowlist-validated via Arcadic.Identifier.validate_url/1 before interpolation (neither command can bind a URL param), value-free on rejection ({:error, :invalid_url}).
  • Arcadic.Conn.with_bearer/2 — derive a Bearer-token-authenticated connection from a session token (typically Arcadic.Security.login/1's); HTTP-only.
  • Arcadic.Schemastats/1 (schema:stats, per-database operation counters), dictionary/1 (schema:dictionary), and materialized_views/1 (schema:materializedviews) (all + !), @props-stripped like the rest of the module.
  • Every Arcadic.Server / Arcadic.Security / Arcadic.Backup call emits a value-free [:arcadic, :admin, :start | :stop | :exception] telemetry span (metadata: :operation, :reason — no database name, setting key/value, URL, or credential).
  • Arcadic.command/4 / command_async/4 accept an :auto_commit boolean opt, forwarded as-is to ArcadeDB's autoCommit body param.
  • Arcadic.FullText — full-text search: FULL_TEXT (Lucene) index DDL (create_index/4 + drop_index/3, retro-indexes existing rows) and SEARCH_INDEX/SEARCH_FIELDS query builders (search/5, search_fields/5, :with_score projects the BM25 $score). HTTP-only SQL.
  • Arcadic.Bulkingest/3 (+ !): bulk-creates vertices and edges over ArcadeDB's POST /api/v1/batch/<db> NDJSON endpoint in one atomic POST (edges wire via a structural "@id" temp key on vertex records; the response's id_mapping maps each temp "@id" to its assigned real RID). Create-only, HTTP-only, via a new optional batch_ingest/3 transport callback.
  • Arcadic.Paramint8/1 / bytes/1: typed param-value wrappers ($int8/$bytes) decoded server-side to a byte[] before the query runs. HTTP-only, requires ArcadeDB ≥ 26.5.1.
  • Arcadic.Vector.fuse/3 now accepts heterogeneous neighbor specs — dense (bare 4-tuple), :sparse, and :fulltext — fused together in one hybrid-ranked result set.
  • New notebook notebooks/graphrag.livemd — an end-to-end graphRAG walkthrough (bulk ingest, idempotent UNWIND upsert, dense/sparse/full-text/hybrid retrieval, INT8 params, traversal).

Fixed

  • Docs: SQL :name vs Cypher $name parameter binding is now documented; the Bolt-streaming example uses Arcadic.Transport.Bolt.setup/1 (the prior transport_options: [bolt: …] form returned :not_supported for streaming); ~> 0.5 install pins; hexdoc module groups.

Notes

  • Re-verified boltx temporal decode against ArcadeDB 26.8.1 (:integration_bolt green).

[0.4.0] - 2026-07-07

Added

  • Arcadic.Schema — tenant-blind schema introspection: types/1, properties/2, indexes/2 (with a :type filter), buckets/1, and database/1 (the engine config, schema:database) (all + !). SQL-only SELECT FROM schema:*; a caller type name binds as a $param and is Identifier-shape-guarded (value-free); ArcadeDB's @props serializer noise is deep-stripped at every nesting depth.
  • Arcadic.Importdatabase/3 (+ !) wrapping IMPORT 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-parens WITH k = v grammar. Import errors are reflected faithfully — a private/loopback host trips ArcadeDB's SSRF guard (:unauthorized / java.lang.SecurityException, distinct from an auth failure's ServerSecurityException via error.exception).
  • Arcadic.Exportdatabase/3 (+ !) wrapping EXPORT DATABASE file://<name>, symmetric to Arcadic.Import: the bare export name is path-traversal-guarded (value-free), and with: reuses the same number/boolean/string settings grammar.
  • HTTP query_stream pages WHERE-less SQL by an O(n) @rid keyset cursor (offset fallback for WHERE'd statements) and supports Cypher streaming via a caller order_key: "id(v)" (offset, $name placeholders). The comment guard is language-aware (// rejected for Cypher). Inside transaction/3 over Bolt, streaming uses the O(n) in-transaction cursor.
  • Transaction-scoped Bolt streaming — query_stream/4 inside transaction/3 streams over the transaction's own connection (sees uncommitted writes), guarded so an execute cannot interleave an open cursor on the shared socket; the cursor callbacks disconnect on a wire fault (desync-safe). Consume the stream inside the transaction/3 body (it is bound to the tx connection).
  • Bolt over TLS — scheme: "bolt+s" with ssl_opts. bolt+s is secure by default (verify_peer against the OS trust store, via boltx's inverted bolt+ssc scheme under the hood); ssl_opts: [verify: :verify_none] is an explicit caller opt-in to skip verification. A :uri opt 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_PORT environment 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, and query_stream/4 now reject a non-keyword-list opts with a value-free ArgumentError ("opts must be a keyword list"). Previously an improper-list opts (e.g. [:foo]) raised a Keyword error 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, and Vector surfaces.

Notes

  • Documented an upstream ArcadeDB server hazard for operators running Bolt over TLS: the listener performed TLS handshakes on its single shared accept thread, so one early-closed, stalled, or untrusted-cert handshake could wedge Bolt for every client (~100% CPU tight loop or no ServerHello) until restart. arcadic's client-side TLS is unaffected. Reported by arcadic; root-caused and fixed upstream on main 2026-07-08 (per-connection handshake threads + read timeout), shipping in 26.7.2 — ArcadeData/arcadedb#5106. Builds predating the fix remain affected; the wedge is condition-dependent (a cleanly-delivered unknown_ca alert does not trigger it — early-close/stall do), so a clean probe on a pre-fix build proves nothing.

[0.3.0] - 2026-07-05

Added

  • Arcadic.Vector sparse + hybrid completion:
    • create_sparse_index/5 (+ !), drop_sparse_index/4 (+ !), sparse_neighbors/8 (+ !) over ArcadeDB LSM_SPARSE_VECTOR indexes ((tokens, weights) pair; rows ranked by top-level score). create_sparse_index opts: dimensions, modifier (:none | :idf).
    • filter (param-bound candidate RID set), group_by, and group_size opts on neighbors/6, sparse_neighbors/8, and fuse/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.md bulk-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 ArcadeDB LSM_VECTOR indexes: create_dense_index/5 (+ !), drop_dense_index/3 (+ !), neighbors/6 (+ !), fuse/3 (+ !), and index_ref/2. Tenant-blind; the query vector, k, ef_search, and max_distance bind as params; index refs are identifier-validated; metadata keys/values and query/fusion option inputs are allowlisted and validated value-free (similarity, encoding, quantization, fusion against their ArcadeDB enums). neighbors/6 rows carry a distance whose scale depends on the index similarity (COSINE 0..1 ascending; DOT_PRODUCT negative); fuse/3 rows are ranked by score. Sparse retrieval and the Ash-native surface are named non-goals.

[0.1.0] - 2026-07-04

Added

Fixed

  • Arcadic.Transport.Bolt now threads conn.database into every Bolt RUN/BEGIN, so with_database/2 selects the database on Bolt (was hitting the connection default).
  • Bolt transaction/3 maps a commit-failure to a typed %Arcadic.Error{reason: :transaction_error} instead of leaking DBConnection's bare :rollback atom.
  • Arcadic.Transport.Bolt — a failed Bolt connect (wrong password, or a Bolt conn pointed at a non-Bolt port) no longer leaks a :gen_tcp socket. 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 by connect_timeout. Connect-time errors are redacted on both sites: a HELLO response arcadic's parser cannot classify returns a value-free :bolt_protocol_error instead 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.