Packages

A lean, framework-agnostic Elixir client for ArcadeDB over the HTTP Cypher command API.

Current section

Files

Jump to
arcadic usage-rules.md
Raw

usage-rules.md

# arcadic usage rules
_A framework-agnostic Elixir client for ArcadeDB over the HTTP Cypher command API._
## What arcadic is (and is not)
- **Is:** a thin transport. Sends Cypher/SQL to ArcadeDB's HTTP command API,
manages connections and session transactions, normalizes responses.
- **Is not:** Ash-aware, tenant-aware, or classification-aware. Never put
multitenancy or sensitive-data logic here — that is `ash_arcadic`'s job.
## Public surface
- **`Arcadic`**`connect/3`, `with_database/2`; `query/4` + `query!/4`
(idempotent read endpoint), `command/4` + `command!/4` (write endpoint),
`command_async/4` (fire-and-forget, returns `:ok` on 202); `transaction/3` and
`rollback/2` for session transactions. Non-bang calls return `{:ok, rows}` or
`{:error, %Arcadic.Error{} | %Arcadic.TransportError{}}`. Default language is
`"cypher"`; opt into `sql`/`gremlin`/`graphql`/`mongo`/`sqlscript` per call.
- **`Arcadic.Conn`** — a pure-data connection handle (no process). Its `Inspect`
redacts auth and session id.
- **`Arcadic.Server`** — server admin: `create_database/2` (+ `!`),
`drop_database/2` (+ `!`), `database_exists?/2`, `list_databases/1`, `ready?/1`.
- **Migrations**`Arcadic.Migration` (behaviour: `version/0`, `up/1`, `down/1`),
`Arcadic.MigrationRegistry` (`use` + `migrations [...]`), `Arcadic.Migrator`
(`migrate/2`, `status/2`, `rollback/3`, `reset/2`, `pending_migrations/2`),
tracking applied versions in `_arcadic_migrations`.
- **`Arcadic.Vector`** — dense + sparse vector search over ArcadeDB `LSM_VECTOR` /
`LSM_SPARSE_VECTOR`: `create_dense_index/5`, `drop_dense_index/3`, `neighbors/6`,
`fuse/3`, `index_ref/2`, plus `create_sparse_index/5`, `drop_sparse_index/4`,
`sparse_neighbors/8` (all + `!`). Tenant-blind; query vector / tokens / weights / `k` /
`ef_search` / `max_distance` bind as params, index refs are identifier-validated, and
metadata / query / fusion option inputs are allowlisted and validated value-free.
Shared opts on `neighbors` / `sparse_neighbors` / `fuse`: `filter` (non-empty
`#bucket:pos` RID candidate set), `group_by` (`Identifier`-shape-guarded), `group_size`
— all param-bound. `distance` scale is similarity-dependent; `fuse/3` and
`sparse_neighbors/8` rank by `score` (sparse rows carry no `distance`). Create sparse
indexes **before** loading rows — they do not retro-index existing data (a
`[:arcadic, :vector, :sparse_index_preexisting]` telemetry event fires if you do).
- **`Arcadic.Transport`** — the transport behaviour seam; `Arcadic.Transport.HTTP`
(Req/Finch) is the default, `Arcadic.Transport.Bolt` is the optional Bolt one.
- **`Arcadic.Error` / `Arcadic.TransportError`** — the typed error taxonomy.
- **`Arcadic.Telemetry`** — value-free `:telemetry.span/3` spans.
- **`Arcadic.Identifier`** — allowlist identifier validation.
## Bulk loading
- For a **large initial load**, prefer ArcadeDB's index-deferred bulk import over an
`INSERT`/`CREATE EDGE` loop: `Arcadic.command(conn, "IMPORT DATABASE '<url>'", %{}, language:
"sql")` imports CSV / JSON / GraphML / Neo4j / OrientDB exports server-side (the source URL must
be reachable by the server).
- For batched **incremental** writes, wrap them in `transaction/3` (one commit for many
statements) instead of auto-committing each `command/4`.
## Non-negotiable rules
- **Parameters only.** Every dynamic value goes into the request `params` map and
is referenced as `$name` in the statement. Never interpolate a value into a
Cypher/SQL string — that is a query-injection defect. This holds for
`query/4`, `command/4`, `command_async/4`, and inside `transaction/3`.
- **Redact at the boundary.** Errors and logs carry structure only.
`Arcadic.Error` exposes a typed `reason`, `http_status`, and `exception` class;
its `detail` field is quarantined (absent from `message/1` and `inspect/1`).
`Arcadic.TransportError` carries only the value-free reason atom. Never surface
raw parameter values or response rows.
- **Validate identifiers.** Database names and other identifiers reaching a URL
path or statement go through `Arcadic.Identifier.validate/1` first (a failure
carries the invalid-shape fact only, never the offending string). Values are
never identifiers — they ride `params`.
## Bolt transport (optional)
The `Arcadic.Transport.Bolt` adapter (optional `boltx` dependency) runs the query
hot path over Bolt. Start it with `Arcadic.Transport.Bolt.start_link/1`, which
pins Bolt to **v4** (`versions: [4.4, 4.3, 4.2, 4.1]` — ArcadeDB speaks v4;
boltx defaults to v5), uses the non-TLS **`bolt` scheme** (ArcadeDB Bolt is
TLS-disabled by default), and takes `username`/`password`. Pass the connection
reference as `transport: Arcadic.Transport.Bolt, transport_options: [bolt: ref]`.
**Server admin (create/drop/list database) is HTTP-only** — use an HTTP conn for
admin even when queries run over Bolt.
See `AGENTS.md` for the full working rules and the verified ArcadeDB HTTP contract.