{"id":50554990,"url":"https://github.com/giovibal/intreccio","last_synced_at":"2026-06-04T06:02:42.285Z","repository":{"id":361492263,"uuid":"1249483777","full_name":"giovibal/intreccio","owner":"giovibal","description":"An simple graphdb with opencypher support","archived":false,"fork":false,"pushed_at":"2026-05-30T22:42:28.000Z","size":379,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T23:12:56.229Z","etag":null,"topics":["go","golang","graphdb","opencypher"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/giovibal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T18:47:06.000Z","updated_at":"2026-05-30T21:17:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/giovibal/intreccio","commit_stats":null,"previous_names":["giovibal/intreccio"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/giovibal/intreccio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovibal%2Fintreccio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovibal%2Fintreccio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovibal%2Fintreccio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovibal%2Fintreccio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giovibal","download_url":"https://codeload.github.com/giovibal/intreccio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giovibal%2Fintreccio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33891733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","graphdb","opencypher"],"created_at":"2026-06-04T06:02:40.107Z","updated_at":"2026-06-04T06:02:42.271Z","avatar_url":"https://github.com/giovibal.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# intreccio\n\n\u003e **Intreccio** — Italian for *interweaving / interlacing*, and figuratively a\n\u003e *web of relationships* (*intreccio di relazioni*). Which is exactly what a\n\u003e graph is.\n\nAn **embedded**, **single-binary** graph database written in **pure Go** (no cgo)\nthat speaks a useful subset of **openCypher 9**. It targets **OLTP /\nknowledge-graph** workloads: point lookups and few-hop traversals over\nmedium-sized graphs. It is *not* an analytical (OLAP) engine.\n\n\u003e Pre-1.0 and under active development, but functional end-to-end: reads,\n\u003e writes, aggregations (count/sum/avg/min/max/collect), `DISTINCT`,\n\u003e `ORDER BY`/`SKIP`/`LIMIT`, `WITH` chaining, variable-length traversal (trail\n\u003e semantics — no repeated relationships) and Cypher-managed indexes all run\n\u003e through the public API. An optional **Raft-replicated cluster mode** adds high\n\u003e availability (see [Clustering](#clustering-optional-high-availability)).\n\n## Highlights\n\n- **Pure Go, no cgo** — deploys as a single self-contained binary.\n- **Property graph** — nodes with labels and properties, typed relationships\n  with properties.\n- **Pluggable storage** behind a minimal `Store` interface; **BadgerDB** is the\n  default engine (bbolt is a planned alternative adapter).\n- **Order-preserving key encoding** so hot queries become a single ordered range\n  scan, and secondary property indexes support range queries.\n- **ACID transactions** with always-consistent indexes (every mutation updates\n  the base record and all of its index keys in the same transaction).\n- **Rule-based query planner** with an inspectable textual `EXPLAIN`.\n\n## Quickstart\n\n### Install\n\n```bash\n# add the library to your project\ngo get github.com/giovibal/intreccio\n\n# or install the CLI/REPL\ngo install github.com/giovibal/intreccio/cmd/intreccio@latest\n```\n\nPrefer a binary? Prebuilt CLI binaries for Linux, macOS and Windows\n(amd64/arm64) are attached to each\n[GitHub Release](https://github.com/giovibal/intreccio/releases) — download the\none for your platform and verify it against the published `checksums.txt`.\n(Installing from source needs Go 1.26+.)\n\n### Embed it in your program\n\n`Open` (or `OpenInMemory`) returns a `*intreccio.DB`. Run Cypher through `Query`\nfor both reads and writes; pass parameters as a `map[string]any`.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/giovibal/intreccio\"\n)\n\nfunc main() {\n\tdb, err := intreccio.Open(\"data/\") // directory-backed; or intreccio.OpenInMemory()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer db.Close()\n\n\tctx := context.Background()\n\n\t// Write.\n\tif _, err := db.Query(ctx,\n\t\t\"CREATE (p:Person {name: $name, email: $email})\",\n\t\tmap[string]any{\"name\": \"Alice\", \"email\": \"a@b.com\"}); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Read. res.Columns is []string; res.Rows is [][]any in column order.\n\tres, err := db.Query(ctx, `\n\t\tMATCH (p:Person)\n\t\tWHERE p.email = $email\n\t\tRETURN p.name AS name`,\n\t\tmap[string]any{\"email\": \"a@b.com\"})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfor _, row := range res.Rows {\n\t\tfmt.Println(row[0]) // Alice\n\t}\n}\n```\n\n### Use the CLI / REPL\n\nThe `intreccio` binary opens a directory-backed database, or an in-memory one if\nno path is given, and accepts Cypher statements interactively (terminated by `;`)\nor one-shot via `-c`:\n\n```bash\nintreccio                      # interactive REPL on an in-memory database\nintreccio data/                # ... persisted under data/\nintreccio -c \"CREATE (n:Person {name: 'Bob'}) RETURN n.name AS name\"\n```\n\nREPL commands: `:quit` / `:exit` to leave, `:help` for help. Statements can span\nmultiple lines; the terminator is `;` (a `;` inside a string literal is not\ncurrently recognized as a boundary).\n\n### Use the Cypher parser standalone\n\nThe openCypher front-end lives in `query/*` and is importable on its own — a\npure-Go parser, analyzer and planner with **no database attached**. Useful for\nlinting or formatting queries, IDE/tooling, or building on top of the plan tree.\nCallers without an index catalog pass `plan.NoIndexes`:\n\n```go\nimport (\n    \"fmt\"\n\n    \"github.com/giovibal/intreccio/query/parser\"\n    \"github.com/giovibal/intreccio/query/plan\"\n)\n\nq, _ := parser.Parse(\"MATCH (p:Person) WHERE p.age \u003e 30 RETURN p.name AS name\")\nop, _ := plan.Plan(q, plan.NoIndexes)\nfmt.Print(plan.Explain(op))\n// Project(p.name AS name)\n//   Filter(p.age \u003e 30)\n//     NodeByLabelScan(p:Person)\n```\n\n## Clustering (optional, high availability)\n\nFor high availability, intreccio can run as a **Raft-replicated** cluster,\nconfigured entirely from the library. A small quorum of **voters** (3 or 5) holds\nthe data and replicates every write through Raft; additional service instances\njoin as dataless **clients** that forward queries to the voters — so you get HA\nwithout replicating to every instance. Clustering lives in the opt-in `cluster`\npackage and is **linked only when you import it**: programs that use\n`intreccio.Open` stay embedded-only and never pull in Raft. See\n`docs/adr/0007-clustering-raft.md`.\n\n```go\nimport \"github.com/giovibal/intreccio/cluster\"\n\nvoters := []cluster.Peer{\n    {ID: \"n1\", RaftAddr: \"10.0.0.1:7000\", ForwardAddr: \"10.0.0.1:7001\"},\n    {ID: \"n2\", RaftAddr: \"10.0.0.2:7000\", ForwardAddr: \"10.0.0.2:7001\"},\n    {ID: \"n3\", RaftAddr: \"10.0.0.3:7000\", ForwardAddr: \"10.0.0.3:7001\"},\n}\n\n// On each voter (exactly one sets Bootstrap: true to form the cluster):\ndb, err := cluster.Open(cluster.Config{\n    NodeID: \"n1\", DataDir: \"data/n1\",\n    BindAddr: \"10.0.0.1:7000\", ForwardAddr: \"10.0.0.1:7001\",\n    Role: cluster.RoleVoter, Bootstrap: true, Peers: voters,\n})\n\n// On an extra app instance that should share the DB without storing it:\ndb, err := cluster.Open(cluster.Config{\n    NodeID: \"app-7\", Role: cluster.RoleClient, Peers: voters,\n})\n\n// Same query API. One Cypher write = one linearizable transaction.\ndb.Query(ctx, \"CREATE (n:Person {name: $n})\", map[string]any{\"n\": \"Bob\"})\ndb.Query(ctx, \"MATCH (p:Person) RETURN p.name\", nil)                    // fast local read\ndb.Query(ctx, \"MATCH (p:Person) RETURN p.name\", nil, intreccio.Linearizable()) // strong read\n```\n\nNotes for operators:\n- **Reads** default to fast, possibly slightly stale local snapshots; pass\n  `intreccio.Linearizable()` for read-your-writes (served via the leader).\n- **Writes** are serialized through the leader and committed by a quorum; losing\n  a minority of voters keeps the cluster available, losing a majority halts writes\n  (safety over availability — no split brain).\n- Set `Config.LogOutput` (e.g. `os.Stderr`) for Raft logs and `Config.ApplyTimeout`\n  to tune write/read-barrier deadlines.\n- It uses **full replication** to the quorum (each voter holds the whole graph);\n  sharding is out of scope.\n\n### Try a cluster locally (multiple terminals)\n\nThe CLI can run a cluster node per process behind the `cluster` build tag (it\nlinks Raft, so it is not the default binary). Build it once:\n\n```bash\nmake cluster-cli      # or: go build -tags cluster -o intreccio ./cmd/intreccio\n```\n\nAll nodes share one **peer string** listing every voter as\n`id=raftAddr=forwardAddr`:\n\n```bash\nexport PEERS='n1=127.0.0.1:7001=127.0.0.1:8001,n2=127.0.0.1:7002=127.0.0.1:8002,n3=127.0.0.1:7003=127.0.0.1:8003'\n```\n\nOpen a terminal per node. Without `-c`, each becomes an interactive REPL on that\nnode; statements end with `;`.\n\n```bash\n# terminal 1 — voter n1 (exactly one voter bootstraps the cluster)\n./intreccio -cluster-id n1 -cluster-data /tmp/intreccio/n1 \\\n  -cluster-bind 127.0.0.1:7001 -cluster-forward 127.0.0.1:8001 \\\n  -cluster-bootstrap -cluster-peers \"$PEERS\"\n\n# terminal 2 — voter n2 (same, no -cluster-bootstrap, ports …2)\n./intreccio -cluster-id n2 -cluster-data /tmp/intreccio/n2 \\\n  -cluster-bind 127.0.0.1:7002 -cluster-forward 127.0.0.1:8002 -cluster-peers \"$PEERS\"\n\n# terminal 3 — voter n3 (ports …3)\n./intreccio -cluster-id n3 -cluster-data /tmp/intreccio/n3 \\\n  -cluster-bind 127.0.0.1:7003 -cluster-forward 127.0.0.1:8003 -cluster-peers \"$PEERS\"\n\n# terminal 4+ — a dataless client (needs only id, role and the peers)\n./intreccio -cluster-id c1 -cluster-role client -cluster-peers \"$PEERS\"\n```\n\nType Cypher in any terminal — writes are forwarded to the leader, reads are\nserved by the node you typed into:\n\n```cypher\nCREATE (n:Person {name: 'Bob'}) RETURN n.name AS name;\nMATCH (p:Person) RETURN p.name AS name;\n```\n\n- **One-shot:** append `-c \"QUERY\"` to run a single statement and exit (handy for\n  scripts or quick checks from a client).\n- **Failover:** stopping the leader's terminal (`:quit`, Ctrl-D, or Ctrl-C) drops\n  that node; the remaining voters re-elect and clients keep working (a 3-voter\n  cluster tolerates losing one). Restart it with the same command **without**\n  `-cluster-bootstrap` and it rejoins and catches up.\n- A voter process runs only while its terminal/REPL is open.\n- **Consistency:** the REPL does default (local, possibly slightly stale) reads;\n  strongly-consistent read-your-writes (`intreccio.Linearizable()`) is available\n  through the library API, not as a REPL flag.\n\n## Architecture\n\nThe query pipeline runs entirely in-process, top to bottom:\n\n```\nCypher text\n  → Parser            → AST\n  → Semantic analysis   (scoping, validation, output columns)\n  → Rule-based planner  (anchor selection, filter push-down)\n  → Physical plan       (Volcano/iterator operators)\n  → Executor            (Next())\n  → Graph storage API   (transactional CRUD of nodes/edges/indexes)\n  → Key encoding        (prefixed tables over an ordered KV store)\n  → Storage engine      (BadgerDB; pure Go; ACID)\n```\n\nStorage is keyed so that adjacency and lookups are ordered range scans. Each edge\nis written twice — outgoing (`o`) and incoming (`i`) — so traversal in either\ndirection is proportional to the fan-out, not the graph size. Property values use\nan order-preserving encoding so a secondary index supports both equality and\nrange predicates.\n\n## Project layout\n\n```\ncmd/intreccio/            CLI/REPL entrypoint (single binary)\nquery/                    public openCypher front-end (importable standalone)\n  ast/                   AST types\n  parser/                hand-written lexer + recursive-descent/Pratt parser\n  sema/                  semantic analysis\n  plan/                  rule-based planner + EXPLAIN\ninternal/\n  storage/               Store interface + engine adapters\n    codec/               order-preserving key \u0026 value encoding\n    badger/              BadgerDB adapter (default)\n    bolt/                bbolt adapter (optional)\n  catalog/               dictionaries, ID counters, index registry\n  graph/                 model + transactional CRUD + traversal primitives\n  exec/                  executor operators (Volcano) — the engine\ncluster/                 optional Raft-replicated clustering (opt-in)\nintreccio.go             public embeddable API (package intreccio)\ndocs/adr/                architecture decision records\n```\n\nThe public surface is the root `intreccio` package, the `query/*` openCypher\nfront-end (usable without a database), and the opt-in `cluster` package; the\nengine and the write path stay under `internal/`.\n\n## Supported Cypher\n\nRead side:\n- `MATCH` / `OPTIONAL MATCH` with node/relationship patterns, directions, labels\n  and types; `OPTIONAL MATCH` produces null bindings on no-match.\n- `WHERE` with comparisons (`=`, `\u003c\u003e`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`), boolean ops\n  (`AND`/`OR`/`NOT`), property access and label predicates,\n  `IS NULL`/`IS NOT NULL`, `STARTS WITH`/`ENDS WITH`/`CONTAINS`, `IN` with list\n  literals, `CASE WHEN ... THEN ... ELSE ... END` (simple and searched).\n- `RETURN` with projection, aliases, `DISTINCT`, `ORDER BY`, `SKIP`, `LIMIT`.\n- `WITH` chaining and scope reset; `UNWIND list AS x`.\n- `UNION` / `UNION ALL`.\n- Variable-length paths `-[:T*1..3]-\u003e` (trail semantics: no repeated\n  relationships).\n- Aggregations: `count`, `collect`, `sum`, `avg`, `min`, `max` (with `DISTINCT`).\n- Scalar functions: `id`, `labels`, `type`, `keys`, `properties`, `size`,\n  `length`, `head`, `last`, `tail`, `toInteger`, `toFloat`, `toString`,\n  `toUpper`, `toLower`, `trim`, `substring`, `replace`, `split`, `abs`.\n\nWrite side:\n- `CREATE`, `MERGE` (with `ON CREATE SET` / `ON MATCH SET`).\n- `SET`: property assignment (`n.p = v`), label addition (`n:Foo`), map replace\n  (`n = {...}`) or merge (`n += {...}`).\n- `REMOVE`: property (`n.p`) or labels (`n:Foo`).\n- `DELETE` / `DETACH DELETE`.\n- `CREATE INDEX FOR (v:Label) ON (v.prop)` with on-demand backfill.\n- Parameters (`$name`).\n\n**Out of scope:** OLAP/vectorized execution, `shortestPath`/`allShortestPaths`,\nsubqueries (`EXISTS { }`, `CALL { }`), user-defined procedures, path variables\n(`p = (...)`) and full TCK conformance.\n\n## Versioning\n\nReleases follow [Semantic Versioning](https://semver.org/) with a `v` prefix\n(`vMAJOR.MINOR.PATCH`). The project is **pre-1.0**: within the `0.x` range a\n**minor** bump may include breaking changes and a **patch** bump is reserved for\nfixes and backward-compatible additions. The **on-disk storage format is not yet\nfrozen**, so a database created by one `0.x` version is not guaranteed to be\nreadable by another. A release is cut by pushing a tag; see\n`docs/adr/0006-versioning-and-release.md` for the rationale.\n\n## Documentation\n\n- `DESIGN.md` — high-level architecture (source of truth).\n- `docs/adr/` — architecture decision records.\n\nAll documentation and code (identifiers, comments and strings) is in English.\n\n## Development\n\nBuilding from source and running the test suite require Go 1.26+:\n\n```bash\ngo build ./...          # build everything\ngo test ./...           # run the tests\nmake race               # tests with the race detector\nmake lint               # golangci-lint (v2)\ngo run ./cmd/intreccio  # run the REPL from source\n```\n\nClustering is behind a build tag so the default binary stays embedded-only and\nRaft-free; build the cluster-capable CLI with `go build -tags cluster\n./cmd/intreccio`. See `DESIGN.md` for the architecture and `docs/adr/` for the\ndesign decisions.\n\n## License\n\nReleased under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiovibal%2Fintreccio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiovibal%2Fintreccio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiovibal%2Fintreccio/lists"}