diff --git a/.github/branch-protection.json b/.github/branch-protection.json
index c039e32..aa1ab19 100644
--- a/.github/branch-protection.json
+++ b/.github/branch-protection.json
@@ -5,7 +5,6 @@
"contexts": [
"Classify Changes",
"Check AGENTS.md Links",
- "Test Workspace",
"Test omnigraph-server --features aws",
"CODEOWNERS matches source",
"CODEOWNERS not hand-edited"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 56ef3e3..fca08da 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -128,6 +128,23 @@ jobs:
test:
name: Test Workspace
needs: classify_changes
+ # PR latency: the full workspace + failpoints build/test is the slowest
+ # gate (~15min warm, up to the 75min ceiling cold) and dominated PR
+ # turnaround. It now runs only on push to `main` (post-merge), on tags,
+ # and on manual `workflow_dispatch` — NOT on pull_request. Trade-off
+ # accepted deliberately: a regression is caught on the `main` run after
+ # merge rather than before it, so `main` can briefly go red. Mitigations:
+ # (1) `Test Workspace` is removed from required PR checks in
+ # `.github/branch-protection.json` (a required check that never
+ # reports would leave every PR permanently pending);
+ # (2) run the full suite locally before merging risky changes
+ # (`cargo test --workspace --locked`), or trigger this workflow via
+ # the Actions "Run workflow" button (workflow_dispatch) on your branch;
+ # (3) openapi.json is no longer auto-regenerated on PRs (that step lived
+ # here) — regenerate it locally for server/API changes
+ # (`OMNIGRAPH_UPDATE_OPENAPI=1 cargo test -p omnigraph-server --test openapi`)
+ # or the strict drift check fails the post-merge `main` run.
+ if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
# 75, not 45: a cold rust-cache (every Cargo.lock change) costs a full
# workspace + failpoints-feature build on a 2-core runner, which now
@@ -274,6 +291,9 @@ jobs:
rustfs_integration:
name: RustFS S3 Integration
+ # `needs: test` means this is push-/dispatch-only too: on pull_request the
+ # `test` job is skipped, so this dependent is skipped with it. S3
+ # integration runs post-merge on `main`, alongside the workspace suite.
needs:
- classify_changes
- test
diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml
index 9484b98..4fac941 100644
--- a/.github/workflows/publish-crates.yml
+++ b/.github/workflows/publish-crates.yml
@@ -1,6 +1,6 @@
name: Publish to crates.io
-# Publishes the four workspace crates to crates.io in dependency order.
+# Publishes the publishable workspace crates to crates.io in dependency order.
#
# Triggers:
# - push of any v* tag (future releases auto-publish alongside release.yml)
@@ -115,10 +115,14 @@ jobs:
# Order matters: each crate must precede anything that depends on it.
# omnigraph-compiler and omnigraph-policy have no internal deps;
- # omnigraph-engine depends on both; server depends on engine + the
- # two leaf crates; cli depends on everything.
+ # omnigraph-engine depends on both; omnigraph-api-types and
+ # omnigraph-cluster depend on engine (+ compiler); server depends on
+ # engine + api-types + cluster + the two leaf crates; cli depends on
+ # everything.
publish_if_new omnigraph-compiler
publish_if_new omnigraph-policy
publish_if_new omnigraph-engine
+ publish_if_new omnigraph-api-types
+ publish_if_new omnigraph-cluster
publish_if_new omnigraph-server
publish_if_new omnigraph-cli
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a265c40..4b9456a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,17 +1,34 @@
name: Release
+# Build per-platform binaries in a matrix, then publish the GitHub release ONCE
+# from a single job. The matrix used to call `softprops/action-gh-release`
+# concurrently — three jobs racing to create/finalize the same release, which
+# exhausted the action's finalize retries and dropped whole platforms' assets.
+# The matrix now only uploads workflow artifacts; `publish_release` is the sole
+# writer of the release (no race).
+#
+# Triggers:
+# - push of a v* tag (normal release)
+# - workflow_dispatch with an explicit `tag` (re-publish a past tag without
+# re-cutting it; resolves the same `${{ inputs.tag || github.ref_name }}`)
+
on:
push:
tags:
- "v*"
workflow_dispatch:
+ inputs:
+ tag:
+ description: "Tag to (re)publish (e.g. v0.7.0). Required for manual dispatches."
+ required: true
+ type: string
jobs:
build_release:
name: Build ${{ matrix.asset_name }}
runs-on: ${{ matrix.runner }}
permissions:
- contents: write
+ contents: read
strategy:
fail-fast: false
matrix:
@@ -27,6 +44,8 @@ jobs:
steps:
- name: Checkout source
uses: actions/checkout@v5.0.1
+ with:
+ ref: ${{ inputs.tag || github.ref_name }}
- name: Install Linux dependencies
if: runner.os == 'Linux'
@@ -81,20 +100,46 @@ jobs:
throw "Windows release archive is missing expected binaries"
}
- - name: Publish GitHub release assets
+ # Upload artifacts only — the single `publish_release` job attaches them to
+ # the release, so no two jobs ever write the release concurrently.
+ - name: Upload build artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.asset_name }}
+ path: |
+ ${{ matrix.asset_name }}.*
+ if-no-files-found: error
+ retention-days: 1
+
+ publish_release:
+ name: Publish GitHub release
+ needs: build_release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Download all build artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: dist
+ merge-multiple: true
+
+ - name: Publish release (single writer — no matrix race)
uses: softprops/action-gh-release@v2.5.0
with:
- files: |
- ${{ matrix.asset_name }}.*
+ tag_name: ${{ inputs.tag || github.ref_name }}
+ files: dist/**
+ overwrite_files: true
update_homebrew_tap:
name: Update Homebrew tap
- needs: build_release
+ needs: publish_release
runs-on: ubuntu-latest
permissions:
contents: read
env:
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
+ RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
steps:
- name: Skip if HOMEBREW_TAP_TOKEN is not configured
if: env.HOMEBREW_TAP_TOKEN == ''
@@ -105,6 +150,8 @@ jobs:
- name: Checkout source
if: env.HOMEBREW_TAP_SKIP != '1'
uses: actions/checkout@v5.0.1
+ with:
+ ref: ${{ env.RELEASE_TAG }}
- name: Checkout Homebrew tap
if: env.HOMEBREW_TAP_SKIP != '1'
@@ -119,7 +166,7 @@ jobs:
env:
GH_TOKEN: ${{ github.token }}
run: |
- ./scripts/update-homebrew-formula.sh "${GITHUB_REF_NAME}" homebrew-tap/Formula/omnigraph.rb
+ ./scripts/update-homebrew-formula.sh "${RELEASE_TAG}" homebrew-tap/Formula/omnigraph.rb
# Diagnostic only: brew is not on PATH on the ubuntu runner by default, so
# set it up explicitly. Both this setup and the audit below are best-effort
@@ -158,22 +205,26 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Formula/omnigraph.rb
- git commit -m "Update Omnigraph formula to ${GITHUB_REF_NAME}"
+ git commit -m "Update Omnigraph formula to ${RELEASE_TAG}"
git push origin HEAD:main
smoke_windows_installer:
name: Smoke Windows installer
- needs: build_release
- if: startsWith(github.ref, 'refs/tags/v')
+ needs: publish_release
+ if: ${{ inputs.tag != '' || startsWith(github.ref, 'refs/tags/v') }}
runs-on: windows-latest
permissions:
contents: read
+ env:
+ RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
steps:
- name: Checkout source
uses: actions/checkout@v5.0.1
+ with:
+ ref: ${{ env.RELEASE_TAG }}
- name: Install from tagged release
- run: ./scripts/install.ps1 -Version "$env:GITHUB_REF_NAME" -InstallDir "$env:RUNNER_TEMP/omnigraph-bin"
+ run: ./scripts/install.ps1 -Version "$env:RELEASE_TAG" -InstallDir "$env:RUNNER_TEMP/omnigraph-bin"
- name: Smoke installed binaries
run: |
diff --git a/AGENTS.md b/AGENTS.md
index d9e0c45..378de88 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -17,8 +17,8 @@ Tools that support `@`-imports (Claude Code) auto-include all three files via th
`CLAUDE.md` is a symlink to this file — there is exactly one source of truth. Edit `AGENTS.md`.
**Version surveyed:** 0.7.0
-**Workspace crates:** `omnigraph-compiler`, `omnigraph` (engine), `omnigraph-policy`, `omnigraph-cluster`, `omnigraph-cli`, `omnigraph-server`
-**Storage substrate:** Lance 6.x (columnar, versioned, branchable)
+**Workspace crates:** `omnigraph-compiler`, `omnigraph` (engine), `omnigraph-policy`, `omnigraph-api-types` (shared HTTP wire DTOs), `omnigraph-cluster`, `omnigraph-cli`, `omnigraph-server`
+**Storage substrate:** Lance 7.x (columnar, versioned, branchable)
**License:** MIT
**Toolchain:** Rust stable, edition 2024
@@ -33,8 +33,8 @@ OmniGraph is a typed property-graph engine built as a coordination layer over ma
- **Multi-modal querying**: vector ANN (`nearest`), full-text (`search`/`fuzzy`/`match_text`/`bm25`), Reciprocal Rank Fusion (`rrf`), and graph traversal (`Expand`, anti-join `not { … }`) in one runtime.
- **Branches and commits across the whole graph**: Git-style — every successful publish appends to a commit DAG; merges are three-way at the row level.
- **Atomic per-query writes**: `mutate_as` and `load` accumulate insert/update batches into an in-memory `MutationStaging.pending` per touched table; one `stage_*` + `commit_staged` per table runs at end-of-query, then `ManifestBatchPublisher::publish` commits the manifest atomically with per-table `expected_table_versions` CAS. A mid-query failure leaves Lance HEAD untouched on staged tables — no drift, no run state machine, no staging branches. Deletes still inline-commit; D₂ at parse time prevents inserts/updates and deletes from coexisting in one query.
-- **HTTP server**: Axum + utoipa OpenAPI, bearer auth (SHA-256 hashed, optional AWS Secrets Manager). Cedar policy enforcement is engine-wide — every `_as` writer calls `Omnigraph::enforce(action, scope, actor)`, so HTTP, CLI, and embedded SDK consumers all hit the same gate. **Two modes** (v0.6.0+): single-graph (legacy flat routes) and multi-graph (`/graphs/{graph_id}/...` cluster routes + read-only `GET /graphs` enumeration). Per-graph + server-level Cedar policies. Multi-graph mode boots from a cluster directory (`--cluster
`, RFC-005) or the legacy `omnigraph.yaml` `graphs:` map. Runtime add/remove (`POST /graphs`, `DELETE /graphs/{id}`) is not exposed — operators run `cluster apply` (or edit the legacy file) and restart.
-- **CLI** with two-surface config (RFC-008): the team-owned cluster directory (`cluster.yaml`) plus the per-operator `~/.omnigraph/config.yaml` (servers, credentials, actor, aliases). The legacy combined `omnigraph.yaml` still loads with per-key deprecation warnings — `config migrate` proposes the split, `OMNIGRAPH_NO_LEGACY_CONFIG=1` enforces strict mode. **Never extend `omnigraph.yaml`.** Multi-format output (json/jsonl/csv/kv/table).
+- **HTTP server**: Axum + utoipa OpenAPI, bearer auth (SHA-256 hashed, optional AWS Secrets Manager). Cedar policy enforcement is engine-wide — every `_as` writer calls `Omnigraph::enforce(action, scope, actor)`, so HTTP, CLI, and embedded SDK consumers all hit the same gate. **Cluster-only boot** (RFC-011): the server always boots from a cluster directory (`--cluster `, RFC-005) and serves N graphs (N ≥ 1) under multi-graph routes (`/graphs/{graph_id}/...` + read-only `GET /graphs` enumeration); there are no single-graph flat routes and no positional-URI boot. Per-graph + server-level Cedar policies. Runtime add/remove (`POST /graphs`, `DELETE /graphs/{id}`) is not exposed — operators run `cluster apply` and restart.
+- **CLI** with two-surface config (RFC-007/008): the team-owned cluster directory (`cluster.yaml`) plus the per-operator `~/.omnigraph/config.yaml` (servers, clusters, credentials, actor, profiles, aliases, defaults). Graphs are addressed via `--store`/`--server`/`--cluster`/`--profile`/operator defaults (RFC-011). Multi-format output (json/jsonl/csv/kv/table).
Throughout the docs, capabilities are split into **L1 — Inherited from Lance** vs **L2 — Added by OmniGraph**.
@@ -53,7 +53,7 @@ CLI (omnigraph) HTTP Server (omnigraph-server, Axum)
omnigraph (engine) ── ManifestCoordinator, CommitGraph, RunRegistry, GraphIndex (CSR/CSC), exec
│
▼
- Lance 6.x ── columnar Arrow, fragments, per-dataset versions/branches, indexes
+ Lance 7.x ── columnar Arrow, fragments, per-dataset versions/branches, indexes
│
▼
Object store (file / s3 / RustFS / MinIO / S3-compat)
@@ -73,32 +73,38 @@ Full diagram and concurrency model: [docs/dev/architecture.md](docs/dev/architec
| **Lance docs index — fetch upstream Lance docs by problem domain** | **[docs/dev/lance.md](docs/dev/lance.md)** |
| **Test coverage map — what's covered, what helpers to reuse, before-every-task checklist** | **[docs/dev/testing.md](docs/dev/testing.md)** |
| Architecture, L1/L2 framing, concurrency model | [docs/dev/architecture.md](docs/dev/architecture.md) |
-| Storage layout, `__manifest` schema, URI schemes, S3 env vars | [docs/user/storage.md](docs/user/storage.md) |
-| `.pg` schema language, types, constraints, annotations, migration planning | [docs/user/schema-language.md](docs/user/schema-language.md) |
-| Schema-lint codes (`OG-XXX-NNN`), families, severity, suppression | [docs/user/schema-lint.md](docs/user/schema-lint.md) |
-| `.gq` query language, MATCH/RETURN/ORDER, search funcs, mutations, IR ops, lint codes | [docs/user/query-language.md](docs/user/query-language.md) |
-| Indexes (BTREE / inverted / vector / graph topology) | [docs/user/indexes.md](docs/user/indexes.md) |
-| Embeddings (compiler + engine clients, env vars, `@embed`) | [docs/user/embeddings.md](docs/user/embeddings.md) |
-| Branches, commit graph, snapshots, system branches | [docs/user/branches-commits.md](docs/user/branches-commits.md) |
-| Transactions and atomicity (per-query atomic; branches as multi-query transactions) | [docs/user/transactions.md](docs/user/transactions.md) |
+| Storage layout, `__manifest` schema, URI schemes, S3 env vars | [docs/user/concepts/storage.md](docs/user/concepts/storage.md) |
+| `.pg` schema language, types, constraints, annotations, migration planning | [docs/user/schema/index.md](docs/user/schema/index.md) |
+| Schema-lint codes (`OG-XXX-NNN`), families, severity, suppression | [docs/user/schema/lint.md](docs/user/schema/lint.md) |
+| `.gq` query language, MATCH/RETURN/ORDER, IR ops, lint codes | [docs/user/queries/index.md](docs/user/queries/index.md) |
+| Mutations — insert/update/delete, D2, atomicity | [docs/user/mutations/index.md](docs/user/mutations/index.md) |
+| Search funcs (`nearest`/`bm25`/`rrf`), hybrid ranking | [docs/user/search/index.md](docs/user/search/index.md) |
+| Indexes (BTREE / inverted / vector / graph topology) | [docs/user/search/indexes.md](docs/user/search/indexes.md) |
+| Embeddings (engine client, env vars, `@embed`) | [docs/user/search/embeddings.md](docs/user/search/embeddings.md) |
+| Concepts — what OmniGraph is, L1/L2 framing | [docs/user/concepts/index.md](docs/user/concepts/index.md) |
+| Quickstart — init → load → query → branch | [docs/user/quickstart.md](docs/user/quickstart.md) |
+| Branches, commit graph, system branches | [docs/user/branching/index.md](docs/user/branching/index.md) |
+| Snapshots & time travel | [docs/user/branching/time-travel.md](docs/user/branching/time-travel.md) |
+| Three-way merge and conflict kinds (user-facing) | [docs/user/branching/merge.md](docs/user/branching/merge.md) |
+| Transactions and atomicity (per-query atomic; branches as multi-query transactions) | [docs/user/branching/transactions.md](docs/user/branching/transactions.md) |
| Direct-publish write path (staging, D2, recovery sidecars; the former Run state machine) | [docs/dev/writes.md](docs/dev/writes.md) |
| Three-way merge and conflict kinds | [docs/dev/merge.md](docs/dev/merge.md) |
-| Diff / change feed (`diff_between`, `diff_commits`) | [docs/user/changes.md](docs/user/changes.md) |
+| Diff / change feed (`diff_between`, `diff_commits`) | [docs/user/branching/changes.md](docs/user/branching/changes.md) |
| Query execution, mutation execution, bulk loader, `load` vs `ingest` | [docs/dev/execution.md](docs/dev/execution.md) |
-| `optimize` (compaction) and `cleanup` (version GC) | [docs/user/maintenance.md](docs/user/maintenance.md) |
-| Cluster operator guide (deploy/manage clusters, approvals, recovery, serving) | [docs/user/cluster.md](docs/user/cluster.md) |
-| Cedar policy actions, scopes, CLI | [docs/user/policy.md](docs/user/policy.md) |
-| HTTP server endpoints, auth, error model, body limits | [docs/user/server.md](docs/user/server.md) |
-| CLI quick-start | [docs/user/cli.md](docs/user/cli.md) |
-| CLI command surface and config schemas (`~/.omnigraph/config.yaml`, legacy `omnigraph.yaml`) | [docs/user/cli-reference.md](docs/user/cli-reference.md) |
-| Audit / actor tracking | [docs/user/audit.md](docs/user/audit.md) |
-| Error taxonomy and result serialization | [docs/user/errors.md](docs/user/errors.md) |
+| `optimize` (compaction) and `cleanup` (version GC) | [docs/user/operations/maintenance.md](docs/user/operations/maintenance.md) |
+| Cluster operator guide (deploy/manage clusters, approvals, recovery, serving) | [docs/user/clusters/index.md](docs/user/clusters/index.md) |
+| Cedar policy actions, scopes, CLI | [docs/user/operations/policy.md](docs/user/operations/policy.md) |
+| HTTP server endpoints, auth, error model, body limits | [docs/user/operations/server.md](docs/user/operations/server.md) |
+| CLI quick-start | [docs/user/cli/index.md](docs/user/cli/index.md) |
+| CLI command surface and config schema (`~/.omnigraph/config.yaml`) | [docs/user/cli/reference.md](docs/user/cli/reference.md) |
+| Audit / actor tracking | [docs/user/operations/audit.md](docs/user/operations/audit.md) |
+| Error taxonomy and result serialization | [docs/user/operations/errors.md](docs/user/operations/errors.md) |
| Install (binary / Homebrew / source / channels) | [docs/user/install.md](docs/user/install.md) |
-| Deployment (binary / container / RustFS bootstrap / auth / build variants) | [docs/user/deployment.md](docs/user/deployment.md) |
+| Deployment (binary / container / S3-local testing / auth / build variants) | [docs/user/deployment.md](docs/user/deployment.md) |
| CI / release workflows | [docs/dev/ci.md](docs/dev/ci.md) |
| Code ownership (CODEOWNERS source of truth, roles, regeneration) | [docs/dev/codeowners.md](docs/dev/codeowners.md) |
| Branch protection policy (declarative, applied via `scripts/apply-branch-protection.sh`) | [docs/dev/branch-protection.md](docs/dev/branch-protection.md) |
-| Constants & tunables cheat sheet | [docs/user/constants.md](docs/user/constants.md) |
+| Constants & tunables cheat sheet | [docs/user/reference/constants.md](docs/user/reference/constants.md) |
| Per-version release notes | [docs/releases/](docs/releases/) |
---
@@ -138,6 +144,7 @@ These are architectural rules that need to be in scope on every change. They're
4. **Bearer-token plaintext never persists in process memory.** Tokens are hashed at startup; auth uses constant-time comparison; the actor id is server-resolved from the hash match and must not be settable by the client.
5. **Reads always see the current index state for the branch they're reading.** Indexes track the branch head, not historical snapshots. If you change index lifecycle, preserve this guarantee.
6. **Stable type IDs survive renames.** Schema migration relies on identity that's stable across rename — don't mint new IDs on rename.
+7. **Logical contract over physical state.** Physical state (index coverage, fragment layout, compaction versions, staged writes) is derived and rebuildable; it must never fail a logical operation. Check preconditions against logical state and let reconciliation converge the physical state idempotently — genuine logical conflicts still fail loudly. This is the rule rules 1–6 instantiate; full statement and applications in [docs/dev/invariants.md](docs/dev/invariants.md).
### Deny-list (fast-pass review filter — full reasoning in [docs/dev/invariants.md](docs/dev/invariants.md))
@@ -173,7 +180,7 @@ Rust stable workspace (edition 2024). `protoc` is a build dependency (`brew inst
cargo build --workspace --locked # build everything
cargo test --workspace --locked # the canonical CI gate (matches CI exactly)
cargo run -p omnigraph-cli -- # run the `omnigraph` CLI from source
-cargo run -p omnigraph-server -- --bind 0.0.0.0:8080 # run the server from source
+cargo run -p omnigraph-server -- --cluster --bind 0.0.0.0:8080 # run the server from source
# Run one crate / one test file / one test fn
cargo test -p omnigraph-engine --test traversal # one integration-test file (see docs/dev/testing.md)
@@ -185,7 +192,7 @@ cargo test -p omnigraph-engine --features failpoints --test failpoints # fault
cargo build -p omnigraph-server --features aws # AWS Secrets Manager bearer-token source
```
-S3-backed tests (`s3_storage`, and the S3 paths in server/CLI system tests) **skip** unless `OMNIGRAPH_S3_TEST_BUCKET` + `AWS_*` (incl. `AWS_ENDPOINT_URL_S3` for non-AWS) are set; CI runs them against containerized RustFS. `scripts/local-rustfs-bootstrap.sh` stands up a local S3 environment.
+S3-backed tests (`s3_storage`, and the S3 paths in server/CLI system tests) **skip** unless `OMNIGRAPH_S3_TEST_BUCKET` + `AWS_*` (incl. `AWS_ENDPOINT_URL_S3` for non-AWS) are set; CI runs them against containerized RustFS. To run RustFS/MinIO yourself, see [docs/user/deployment.md](docs/user/deployment.md) → *Testing against S3 locally*.
CI does **not** run `clippy` or `rustfmt` as gates — but `cargo test --workspace --locked` is the exact gate, so run it before pushing. Two non-test CI checks: `scripts/check-agents-md.sh` (doc cross-link integrity — run it after moving/renaming docs) and OpenAPI drift (`crates/omnigraph-server/tests/openapi.rs` regenerates `openapi.json`; set `OMNIGRAPH_UPDATE_OPENAPI=1` to update the checked-in copy when a server/API change is intentional).
@@ -203,9 +210,9 @@ omnigraph load --data ./seed.jsonl --mode overwrite s3://my-bucket/graph.omni
# Load a review batch onto its own branch (--from forks it if missing)
omnigraph load --branch review/2026-04-25 --from main --mode merge --data ./batch.jsonl s3://my-bucket/graph.omni
-# Run a hybrid (vector + BM25) query
-omnigraph read --query ./queries.gq --name find_similar \
- --params '{"q":"trends in AI safety"}' --format table s3://my-bucket/graph.omni
+# Run a hybrid (vector + BM25) query — ad-hoc .gq against a store (positional = query name)
+omnigraph query --query ./queries.gq find_similar \
+ --params '{"q":"trends in AI safety"}' --format table --store s3://my-bucket/graph.omni
# Plan + apply schema migration
omnigraph schema plan --schema ./next.pg s3://my-bucket/graph.omni
@@ -225,10 +232,10 @@ omnigraph cleanup --keep 10 --older-than 7d --confirm s3://my-bucket/graph.omni
# Stand up the HTTP server (token from env)
OMNIGRAPH_SERVER_BEARER_TOKEN=xxxx \
- omnigraph-server s3://my-bucket/graph.omni --bind 0.0.0.0:8080
+ omnigraph-server --cluster s3://my-bucket/cluster --bind 0.0.0.0:8080
# Cedar policy explain
-omnigraph policy explain --actor act-alice --action change --branch main
+omnigraph policy explain --cluster ./company-brain --graph knowledge --actor act-alice --action change --branch main
```
---
@@ -241,10 +248,10 @@ omnigraph policy explain --actor act-alice --action change --branch main
| Per-dataset versioning + time travel | ✅ | `snapshot_at_version`, `entity_at`, snapshot-pinned reads across many tables |
| Per-dataset branches | ✅ | **Graph-level** branches (atomic across all sub-tables), lazy fork, system branch filtering |
| Atomic single-dataset commits | ✅ | **Multi-table publish via three layers**, NOT a single Lance primitive: (1) per-table Lance `commit_staged` for the data write, (2) `__manifest` row-level CAS via `ManifestBatchPublisher` for cross-table ordering, (3) the open-time recovery sweep for the residual gap between (1) and (2). All three layers ship; the five migrated writers (`MutationStaging::finalize`, `schema_apply`, `branch_merge`, `ensure_indices`, `optimize_all_tables`) write a `__recovery/{ulid}.json` sidecar before Phase B and delete it after Phase C. The next `Omnigraph::open` (gated on `OpenMode::ReadWrite`) runs the sweep in `db/manifest/recovery.rs`: classify, decide all-or-nothing per sidecar, roll forward via single `ManifestBatchPublisher::publish` or roll back via `Dataset::restore` followed by a manifest publish of the restored version (so both directions converge to `manifest == HEAD` — no residual drift), and record an audit row in `_graph_commit_recoveries.lance` (queryable via `omnigraph commit list --filter actor=omnigraph:recovery`). The write entry points (`load_as`, `mutate_as`, `apply_schema_as`, `branch_merge_as`) and `refresh` additionally run an in-process roll-forward-only heal (serialized against live writers via the per-table write queues), so a long-lived server converges on its next write without restart; only rollback-eligible sidecars still defer to the next read-write open (a future background reconciler's goal). Engine writes route through a sealed `TableStorage` trait (`db.storage()`) exposing only `stage_*` + `commit_staged` + reads; the inline-commit residuals (`delete_where`, `create_vector_index`) are split onto a separate sealed `InlineCommitResidual` trait reached via `db.storage_inline_residual()` (MR-854), so the default surface cannot couple a write with a HEAD advance — §1 holds by construction. `delete_where` and `create_vector_index` stay inline until upstream Lance ships a public two-phase API ([#6658](https://github.com/lance-format/lance/issues/6658), [#6666](https://github.com/lance-format/lance/issues/6666)); `LoadMode::Overwrite` uses Lance `Overwrite` staged transactions. |
-| Compaction (`compact_files`) | ✅ | `omnigraph optimize` orchestrates over all node/edge tables, bounded concurrency; **publishes each compacted table's new version to `__manifest`** (so the manifest tracks the Lance HEAD — required for reads to observe compaction and for schema apply / strict writes to pass their HEAD-vs-manifest precondition), under the per-`(table, main)` write queue with `SidecarKind::Optimize` recovery coverage; **refuses on an unrecovered graph** (errors if a `__recovery` sidecar is pending); **skips uncovered HEAD > manifest drift** with `DriftNeedsRepair` instead of interpreting it; **skips blob-bearing tables** (reported via `TableOptimizeStats.skipped`, not silent), gated on `LANCE_SUPPORTS_BLOB_COMPACTION` until the upstream blob-v2 compaction-decode bug is fixed (see [docs/dev/invariants.md](docs/dev/invariants.md) Known Gaps) |
+| Compaction (`compact_files`) + reindex (`optimize_indices`) | ✅ | `omnigraph optimize` orchestrates over all node/edge tables, bounded concurrency; per table runs `compact_files` **then Lance `optimize_indices`** (folds appended/rewritten fragments back into existing indexes — incremental merge, not retrain) and **publishes the resulting version to `__manifest`** (so the manifest tracks the Lance HEAD — required for reads to observe the work and for schema apply / strict writes to pass their HEAD-vs-manifest precondition), under the per-`(table, main)` write queue with `SidecarKind::Optimize` recovery coverage spanning both ops; **commits even with no compaction work if index coverage is stale**; **refuses on an unrecovered graph**; **skips uncovered HEAD > manifest drift** with `DriftNeedsRepair`; **skips blob-bearing tables** (reported via `TableOptimizeStats.skipped`, not silent; reindex is skipped for them too today), gated on `LANCE_SUPPORTS_BLOB_COMPACTION` until the upstream blob-v2 compaction-decode bug is fixed (see [docs/dev/invariants.md](docs/dev/invariants.md) Known Gaps) |
| Repair uncovered drift | — | `omnigraph repair` explicitly classifies uncovered table `HEAD > manifest` drift: verified maintenance drift (`ReserveFragments`/`Rewrite`) can be published with `--confirm`; suspicious or unverifiable drift requires `--force --confirm`. Sidecar-covered crash residuals still recover automatically on open. |
| Cleanup (`cleanup_old_versions`) | ✅ | `omnigraph cleanup` with `--keep` / `--older-than` policy |
-| BTREE / inverted (FTS) / vector indexes | ✅ | `ensure_indices` builds them on every relevant column; idempotent; lazy across branches |
+| BTREE / inverted (FTS) / vector indexes | ✅ | `@index`/`@key` declares intent; the physical index is derived state that never fails a logical op. Built per column through one chokepoint (`build_indices_on_dataset_for_catalog`, type-dispatched by `node_prop_index_kind`: enum + orderable scalar → BTREE, free-text String → FTS, Vector → vector); idempotent; lazy across branches. **Schema apply builds nothing** (records intent only); `load`/`mutate` build inline but **defer an untrainable Vector column** (no trainable vectors yet) as *pending* rather than aborting. `ensure_indices`/`optimize` is the reconciler that materializes declared-but-missing indexes and restores coverage of appended/rewritten fragments (`optimize_indices`), reporting still-pending columns (see Compaction row). |
| `merge_insert` upsert | ✅ | `LoadMode::Merge`, mutation `update`/`insert`/`delete` lowering |
| Vector search | ✅ | `nearest()` query op; embedding pipeline (Gemini / OpenAI clients); `@embed` in schema |
| Full-text search | ✅ | `search/fuzzy/match_text/bm25` query ops |
@@ -257,11 +264,12 @@ omnigraph policy explain --actor act-alice --action change --branch main
| Per-query atomic writes | — | In-memory `MutationStaging.pending` accumulator + `stage_*` / `commit_staged` per touched table at end-of-query + publisher CAS via `commit_with_expected` (single manifest commit per `mutate_as` / `load`); D₂ parse-time rule keeps inserts/updates and deletes from mixing |
| Three-way row-level merge | — | `OrderedTableCursor` + `StagedTableWriter`, structured `MergeConflictKind` |
| Change feeds | — | `diff_between` / `diff_commits` with manifest fast path + ID streaming |
-| Cedar policy | — | Per-graph actions plus server-scoped actions (see [docs/user/policy.md](docs/user/policy.md) for the current list), branch / target_branch / protected scopes, validate/test/explain CLI. **Engine-wide enforcement** (MR-722): every `_as` writer (`apply_schema_as`, `mutate_as`, `load_as` — the deprecated `ingest_as` shims route through it — `branch_create_as` / `branch_create_from_as`, `branch_delete_as`, `branch_merge_as`) calls `Omnigraph::enforce(action, scope, actor)` — HTTP, CLI, embedded SDK all hit the same gate. |
-| HTTP server | — | Axum, OpenAPI via utoipa, bearer auth (SHA-256, AWS Secrets Manager option), `authorize_request` at the HTTP boundary (resolves bearer→actor, applies admission control), NDJSON streaming export, **multi-graph mode (v0.6.0+) with cluster routes + read-only `GET /graphs` enumeration + per-graph + server-level Cedar policies. Multi-graph boots from a cluster directory (`--cluster`) or the legacy `omnigraph.yaml`; add/remove graphs via `cluster apply` (or by editing the legacy file) and restarting.** |
-| CLI with config | — | two-surface config (team `cluster.yaml` dir + per-operator `~/.omnigraph/config.yaml`; legacy `omnigraph.yaml` deprecated per RFC-008), aliases, multi-format output (json/jsonl/csv/kv/table) |
+| Cedar policy | — | Per-graph actions plus server-scoped actions (see [docs/user/operations/policy.md](docs/user/operations/policy.md) for the current list), branch / target_branch / protected scopes, validate/test/explain CLI. **Engine-wide enforcement** (MR-722): every `_as` writer (`apply_schema_as`, `mutate_as`, `load_as` — the deprecated `ingest_as` shims route through it — `branch_create_as` / `branch_create_from_as`, `branch_delete_as`, `branch_merge_as`) calls `Omnigraph::enforce(action, scope, actor)` — HTTP, CLI, embedded SDK all hit the same gate. |
+| HTTP server | — | Axum, OpenAPI via utoipa, bearer auth (SHA-256, AWS Secrets Manager option), `authorize_request` at the HTTP boundary (resolves bearer→actor, applies admission control), NDJSON streaming export, **cluster-only boot (RFC-011): always `--cluster `, serving N graphs (N ≥ 1) under multi-graph routes + read-only `GET /graphs` enumeration + per-graph + server-level Cedar policies. Add/remove graphs via `cluster apply` and restart.** |
+| CLI with config | — | two-surface config (team `cluster.yaml` dir + per-operator `~/.omnigraph/config.yaml`), scope addressing (`--store`/`--server`/`--cluster`/`--profile`/defaults, RFC-011), aliases, multi-format output (json/jsonl/csv/kv/table) |
| Audit / actor tracking | — | `_as` write APIs + actor map in commit graph |
-| Local RustFS bootstrap | — | `scripts/local-rustfs-bootstrap.sh` one-shot S3-backed dev environment |
+| Local S3 testing | — | run RustFS/MinIO + the `AWS_*` env; see [docs/user/deployment.md](docs/user/deployment.md) → *Testing against S3 locally* |
+| Agent skill | — | `skills/omnigraph` — operational playbook for driving Omnigraph; install with `npx skills add ModernRelay/omnigraph@omnigraph` |
---
@@ -282,7 +290,7 @@ Rules:
7. **Re-verify before recommending.** If you cite a flag, env var, endpoint, or constant to the user or in code, grep for it in source first. Memory and docs go stale; the code is authoritative.
8. **Keep AGENTS.md short.** This file is always loaded into agent context, so every added line has a recurring context-window cost. Prefer pointers and terse invariants here; put detail in `docs/`.
9. **Keep AGENTS.md a map, not an encyclopedia.** New deep content goes into `docs/`. Add an entry to "Where to find each topic" instead of pasting prose into this file. The "Always-on rules" section is the exception — it's for invariants that should always be in scope.
-10. **Re-read on schema/query/IR changes.** Edits to `schema.pest`, `query.pest`, `ir/lower.rs`, `query/typecheck.rs`, or `query/lint.rs` should trigger a re-read of [docs/user/schema-language.md](docs/user/schema-language.md), [docs/user/query-language.md](docs/user/query-language.md), and [docs/dev/execution.md](docs/dev/execution.md) to confirm they still describe reality.
+10. **Re-read on schema/query/IR changes.** Edits to `schema.pest`, `query.pest`, `ir/lower.rs`, `query/typecheck.rs`, or `query/lint.rs` should trigger a re-read of [docs/user/schema/index.md](docs/user/schema/index.md), [docs/user/queries/index.md](docs/user/queries/index.md), and [docs/dev/execution.md](docs/dev/execution.md) to confirm they still describe reality.
11. **Always make smaller commits.** Each commit does one thing, compiles, and passes tests; mechanical refactors land separately from the behavior changes they enable.
12. **Test-first for bug fixes.** When fixing an identified bug, write a regression test that reproduces the failure first. Confirm it fails against the current code with the predicted symptom (not an unrelated error). Then land the fix in a separate commit and confirm the test turns green. The test commit lands just before the fix commit so the red → green pair is visible in `git log` and a reviewer can check out the test commit alone and reproduce the failure.
13. **Correct by design over symptomatic patches.** When a bug surfaces, identify the root cause and make the fix correct by construction. Don't patch the symptom. If the design admits the bug class, the fix is to close the class, not to add a guard around the latest instance. A symptomatic patch is acceptable only as a stop-gap, with an explicit note in the commit message and a follow-up issue tracking the design fix.
diff --git a/Cargo.lock b/Cargo.lock
index 2099055..2419e9f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -23,9 +23,9 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"cipher",
- "cpufeatures",
+ "cpufeatures 0.2.17",
]
[[package]]
@@ -34,7 +34,7 @@ version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"const-random",
"getrandom 0.3.4",
"once_cell",
@@ -83,9 +83,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.21"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -104,9 +104,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
-version = "0.2.7"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
@@ -137,6 +137,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "ar_archive_writer"
version = "0.5.1"
@@ -708,11 +717,11 @@ dependencies = [
"bytes",
"form_urlencoded",
"hex",
- "hmac",
+ "hmac 0.12.1",
"http 0.2.12",
"http 1.4.0",
"percent-encoding",
- "sha2",
+ "sha2 0.10.9",
"time",
"tracing",
]
@@ -980,7 +989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
dependencies = [
"addr2line",
- "cfg-if",
+ "cfg-if 1.0.4",
"libc",
"miniz_oxide",
"object",
@@ -1071,7 +1080,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -1083,9 +1092,9 @@ dependencies = [
"arrayref",
"arrayvec 0.7.6",
"cc",
- "cfg-if",
+ "cfg-if 1.0.4",
"constant_time_eq",
- "cpufeatures",
+ "cpufeatures 0.2.17",
]
[[package]]
@@ -1097,6 +1106,15 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "block-buffer"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "block-padding"
version = "0.3.3"
@@ -1266,6 +1284,12 @@ dependencies = [
"smol_str",
]
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
[[package]]
name = "cfg-if"
version = "1.0.4"
@@ -1278,6 +1302,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+[[package]]
+name = "chacha20"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
+dependencies = [
+ "cfg-if 1.0.4",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.44"
@@ -1308,15 +1343,15 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
- "crypto-common",
+ "crypto-common 0.1.7",
"inout",
]
[[package]]
name = "clap"
-version = "4.5.58"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
@@ -1324,9 +1359,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.58"
+version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
@@ -1336,9 +1371,9 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.55"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
+checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
@@ -1361,6 +1396,12 @@ dependencies = [
"cc",
]
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
[[package]]
name = "color-eyre"
version = "0.6.5"
@@ -1394,6 +1435,25 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+[[package]]
+name = "colored"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
[[package]]
name = "comfy-table"
version = "7.2.2"
@@ -1436,6 +1496,12 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+[[package]]
+name = "const-oid"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
+
[[package]]
name = "const-random"
version = "0.1.18"
@@ -1456,12 +1522,37 @@ dependencies = [
"tiny-keccak",
]
+[[package]]
+name = "const-str"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18f12cc9948ed9604230cdddc7c86e270f9401ccbe3c2e98a4378c5e7632212f"
+
+[[package]]
+name = "const_panic"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652"
+dependencies = [
+ "typewit",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation"
version = "0.10.1"
@@ -1478,6 +1569,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+[[package]]
+name = "countio"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9702aee5d1d744c01d82f6915644f950f898e014903385464c773b96fefdecb"
+dependencies = [
+ "futures-io",
+]
+
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -1487,6 +1587,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "crc32c"
version = "0.6.8"
@@ -1502,7 +1611,7 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
]
[[package]]
@@ -1574,6 +1683,15 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "crypto-common"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "csv"
version = "1.4.0"
@@ -1595,6 +1713,31 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "ctor"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "424e0138278faeb2b401f174ad17e715c829512d74f3d1e81eb43365c2e0590e"
+dependencies = [
+ "ctor-proc-macro",
+ "dtor",
+]
+
+[[package]]
+name = "ctor-proc-macro"
+version = "0.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
+
+[[package]]
+name = "ctutils"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+dependencies = [
+ "cmov",
+]
+
[[package]]
name = "darling"
version = "0.23.0"
@@ -1635,7 +1778,7 @@ version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"crossbeam-utils",
"hashbrown 0.14.5",
"lock_api",
@@ -1681,7 +1824,7 @@ dependencies = [
"futures",
"itertools 0.14.0",
"log",
- "object_store 0.13.2",
+ "object_store",
"parking_lot",
"rand 0.9.2",
"regex",
@@ -1712,7 +1855,7 @@ dependencies = [
"futures",
"itertools 0.14.0",
"log",
- "object_store 0.13.2",
+ "object_store",
"parking_lot",
"tokio",
]
@@ -1737,7 +1880,7 @@ dependencies = [
"futures",
"itertools 0.14.0",
"log",
- "object_store 0.13.2",
+ "object_store",
]
[[package]]
@@ -1756,7 +1899,7 @@ dependencies = [
"itertools 0.14.0",
"libc",
"log",
- "object_store 0.13.2",
+ "object_store",
"paste",
"sqlparser",
"tokio",
@@ -1797,7 +1940,7 @@ dependencies = [
"glob",
"itertools 0.14.0",
"log",
- "object_store 0.13.2",
+ "object_store",
"rand 0.9.2",
"tokio",
"url",
@@ -1823,7 +1966,7 @@ dependencies = [
"datafusion-session",
"futures",
"itertools 0.14.0",
- "object_store 0.13.2",
+ "object_store",
"tokio",
]
@@ -1845,7 +1988,7 @@ dependencies = [
"datafusion-physical-plan",
"datafusion-session",
"futures",
- "object_store 0.13.2",
+ "object_store",
"regex",
"tokio",
]
@@ -1868,7 +2011,7 @@ dependencies = [
"datafusion-physical-plan",
"datafusion-session",
"futures",
- "object_store 0.13.2",
+ "object_store",
"serde_json",
"tokio",
"tokio-stream",
@@ -1896,7 +2039,7 @@ dependencies = [
"datafusion-physical-expr-common",
"futures",
"log",
- "object_store 0.13.2",
+ "object_store",
"parking_lot",
"rand 0.9.2",
"tempfile",
@@ -1965,7 +2108,7 @@ dependencies = [
"num-traits",
"rand 0.9.2",
"regex",
- "sha2",
+ "sha2 0.10.9",
"unicode-segmentation",
"uuid",
]
@@ -2284,7 +2427,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
- "const-oid",
+ "const-oid 0.9.6",
"pem-rfc7468",
"zeroize",
]
@@ -2311,12 +2454,24 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "block-buffer",
- "const-oid",
- "crypto-common",
+ "block-buffer 0.10.4",
+ "const-oid 0.9.6",
+ "crypto-common 0.1.7",
"subtle",
]
+[[package]]
+name = "digest"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
+dependencies = [
+ "block-buffer 0.12.1",
+ "const-oid 0.10.2",
+ "crypto-common 0.2.2",
+ "ctutils",
+]
+
[[package]]
name = "dirs"
version = "6.0.0"
@@ -2358,6 +2513,21 @@ dependencies = [
"const-random",
]
+[[package]]
+name = "dtor"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301"
+dependencies = [
+ "dtor-proc-macro",
+]
+
+[[package]]
+name = "dtor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
+
[[package]]
name = "dunce"
version = "1.0.5"
@@ -2403,7 +2573,7 @@ version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
]
[[package]]
@@ -2578,9 +2748,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "fsst"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83cf860f6a6bf0a6a60fdfe5a36c75121fad5ea4332d1d12deee3e65b6047727"
+checksum = "bcd0ce0249ac12fd44fcde62d435c36d881952c2f0df4d1de24b45e1dbba5ddb"
dependencies = [
"arrow-array",
"rand 0.9.2",
@@ -2689,6 +2859,15 @@ dependencies = [
"slab",
]
+[[package]]
+name = "gearhash"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8cf82cf76cd16485e56295a1377c775ce708c9f1a0be6b029076d60a245d213"
+dependencies = [
+ "cfg-if 0.1.10",
+]
+
[[package]]
name = "generator"
version = "0.8.8"
@@ -2696,7 +2875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
dependencies = [
"cc",
- "cfg-if",
+ "cfg-if 1.0.4",
"libc",
"log",
"rustversion",
@@ -2720,10 +2899,10 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"js-sys",
"libc",
- "wasi",
+ "wasi 0.11.1+wasi-snapshot-preview1",
"wasm-bindgen",
]
@@ -2733,7 +2912,7 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"js-sys",
"libc",
"r-efi 5.3.0",
@@ -2747,11 +2926,14 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
+ "js-sys",
"libc",
"r-efi 6.0.0",
+ "rand_core 0.10.1",
"wasip2",
"wasip3",
+ "wasm-bindgen",
]
[[package]]
@@ -2760,6 +2942,26 @@ version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
+[[package]]
+name = "git-version"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad568aa3db0fcbc81f2f116137f263d7304f512a1209b35b85150d3ef88ad19"
+dependencies = [
+ "git-version-macro",
+]
+
+[[package]]
+name = "git-version-macro"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "glob"
version = "0.3.3"
@@ -2822,7 +3024,7 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"crunchy",
"num-traits",
"zerocopy",
@@ -2866,6 +3068,12 @@ version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+[[package]]
+name = "heapify"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0049b265b7f201ca9ab25475b22b47fe444060126a51abe00f77d986fc5cc52e"
+
[[package]]
name = "heck"
version = "0.5.0"
@@ -2884,22 +3092,44 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hf-xet"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "430b33fa84f92796d4d263070b6c0d3ca219df7b9a0e1853ee431029b1612bcd"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http 1.4.0",
+ "more-asserts",
+ "serde",
+ "thiserror",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "uuid",
+ "xet-client",
+ "xet-core-structures",
+ "xet-data",
+ "xet-runtime",
+]
+
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
- "digest",
+ "digest 0.10.7",
]
[[package]]
-name = "home"
-version = "0.5.12"
+name = "hmac"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
+checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
dependencies = [
- "windows-sys 0.61.2",
+ "digest 0.11.3",
]
[[package]]
@@ -2975,6 +3205,15 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
+[[package]]
+name = "hybrid-array"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da"
+dependencies = [
+ "typenum",
+]
+
[[package]]
name = "hyper"
version = "0.14.32"
@@ -3073,9 +3312,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2 0.6.2",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
@@ -3271,7 +3512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d09b98f7eace8982db770e4408e7470b028ce513ac28fecdc6bf4c30fe92b62"
dependencies = [
"bitflags",
- "cfg-if",
+ "cfg-if 1.0.4",
"libc",
]
@@ -3329,10 +3570,12 @@ checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
+ "js-sys",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
+ "wasm-bindgen",
"windows-sys 0.61.2",
]
@@ -3362,6 +3605,55 @@ dependencies = [
"jiff-tzdb",
]
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if 1.0.4",
+ "combine",
+ "jni-macros",
+ "jni-sys",
+ "log",
+ "simd_cesu8",
+ "thiserror",
+ "walkdir",
+ "windows-link",
+]
+
+[[package]]
+name = "jni-macros"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "simd_cesu8",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
+dependencies = [
+ "jni-sys-macros",
+]
+
+[[package]]
+name = "jni-sys-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
[[package]]
name = "jobserver"
version = "0.1.34"
@@ -3402,30 +3694,32 @@ dependencies = [
"serde_json",
]
-[[package]]
-name = "jsonwebtoken"
-version = "9.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
-dependencies = [
- "base64",
- "js-sys",
- "pem",
- "ring",
- "serde",
- "serde_json",
- "simple_asn1",
-]
-
[[package]]
name = "keccak"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653"
dependencies = [
- "cpufeatures",
+ "cpufeatures 0.2.17",
]
+[[package]]
+name = "konst"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f660d5f887e3562f9ab6f4a14988795b694099d66b4f5dedc02d197ba9becb1d"
+dependencies = [
+ "const_panic",
+ "konst_proc_macros",
+ "typewit",
+]
+
+[[package]]
+name = "konst_proc_macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e037a2e1d8d5fdbd49b16a4ea09d5d6401c1f29eca5ff29d03d3824dba16256a"
+
[[package]]
name = "lalrpop"
version = "0.22.2"
@@ -3460,10 +3754,11 @@ dependencies = [
[[package]]
name = "lance"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34e854994e84d043897f5ec9fb609221e9e69e3fd52996cd715d979fcd349f6"
+checksum = "3944aca86f4c78f4da04af1c2bf33e664a2826b7af72972ad200d6b9de59019f"
dependencies = [
+ "arc-swap",
"arrow",
"arrow-arith",
"arrow-array",
@@ -3478,9 +3773,11 @@ dependencies = [
"async-trait",
"async_cell",
"aws-credential-types",
+ "bitpacking",
"byteorder",
"bytes",
"chrono",
+ "crossbeam-queue",
"crossbeam-skiplist",
"dashmap",
"datafusion",
@@ -3507,13 +3804,14 @@ dependencies = [
"lance-tokenizer",
"log",
"moka",
- "object_store 0.12.5",
+ "object_store",
"permutation",
"pin-project",
"prost",
"prost-build",
"prost-types",
"rand 0.9.2",
+ "rayon",
"roaring",
"semver",
"serde",
@@ -3529,13 +3827,12 @@ dependencies = [
[[package]]
name = "lance-arrow"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7827fe404358c27d120ee8ea8ef7b9415c2911d54072bec83dd689d750ae65da"
+checksum = "253f4a0a70580c985b91e65e9ca6cad644825a4078de28d8efbacf3ffbd7ecdc"
dependencies = [
"arrow-array",
"arrow-buffer",
- "arrow-cast",
"arrow-data",
"arrow-ipc",
"arrow-ord",
@@ -3552,9 +3849,9 @@ dependencies = [
[[package]]
name = "lance-bitpacking"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cd0b31570d50fe13c7e4e36b03e1f1c99c3d8e5a34845b24b0665b51b40570d"
+checksum = "80c4d12521b1945041dd515a56aa0854973138e7ac12111c92572e33e4ecb593"
dependencies = [
"arrayref",
"paste",
@@ -3563,9 +3860,9 @@ dependencies = [
[[package]]
name = "lance-core"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b128c213c676cb8e03c62a68670642770825171e64097cc2da97cbb19fe35d29"
+checksum = "13f84020da5a484e2f07dd1796e09785ed7cd889857ebc4cb77e32ef214ee594"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -3573,7 +3870,6 @@ dependencies = [
"async-trait",
"byteorder",
"bytes",
- "chrono",
"datafusion-common",
"datafusion-sql",
"deepsize",
@@ -3582,10 +3878,9 @@ dependencies = [
"lance-arrow",
"libc",
"log",
- "mock_instant",
"moka",
"num_cpus",
- "object_store 0.12.5",
+ "object_store",
"pin-project",
"prost",
"rand 0.9.2",
@@ -3602,9 +3897,9 @@ dependencies = [
[[package]]
name = "lance-datafusion"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e03b2de71cbcd09b10bf1a17c83cacbc0176ecd97203fb72b9e59d9b8f9a3743"
+checksum = "7460597a66534a75987993d4dac5bc330586d99c5b79ae73367dbcbd4e29e576"
dependencies = [
"arrow",
"arrow-array",
@@ -3628,16 +3923,15 @@ dependencies = [
"pin-project",
"prost",
"prost-build",
- "snafu",
"tokio",
"tracing",
]
[[package]]
name = "lance-datagen"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fe7c7ea7fd397e495a1646fec360e46ee0cbd75718f1c0e887aad657c5f2944"
+checksum = "046f5506ed2271cd941a050de7bf535dd3aedc291aadec836a63fa56c5926e3b"
dependencies = [
"arrow",
"arrow-array",
@@ -3655,9 +3949,9 @@ dependencies = [
[[package]]
name = "lance-encoding"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe3f8070835b407d8db9ea8728386bc3207ba23c66a9c22d344e231ef12b77ca"
+checksum = "7af54edf43dcf9d6a56cc636eb35d457e68373c6448dca3f0891b3325b4a24e6"
dependencies = [
"arrow-arith",
"arrow-array",
@@ -3682,9 +3976,7 @@ dependencies = [
"num-traits",
"prost",
"prost-build",
- "prost-types",
"rand 0.9.2",
- "snafu",
"strum",
"tokio",
"tracing",
@@ -3694,9 +3986,9 @@ dependencies = [
[[package]]
name = "lance-file"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6dfcf654549330df3aef708cd7c12e170feecddd34d6c19dd005b4153213268"
+checksum = "0772ae2d6207995dc1eb28aff9507f78e90b3362b58f311da001e9dc25f3d736"
dependencies = [
"arrow-arith",
"arrow-array",
@@ -3717,21 +4009,21 @@ dependencies = [
"lance-io",
"log",
"num-traits",
- "object_store 0.12.5",
+ "object_store",
"prost",
"prost-build",
"prost-types",
- "snafu",
"tokio",
"tracing",
]
[[package]]
name = "lance-index"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb8ad0bd10efa2608634a2518b7dd501231e76c56a65fbd6519e23914cc425a"
+checksum = "e71fbfb51096a903cb524fe0da716f5f15fbc4a6b6f84cd6dec21abf319c5e84"
dependencies = [
+ "arc-swap",
"arrow",
"arrow-arith",
"arrow-array",
@@ -3750,7 +4042,6 @@ dependencies = [
"datafusion-common",
"datafusion-expr",
"datafusion-physical-expr",
- "datafusion-sql",
"deepsize",
"dirs",
"fst",
@@ -3772,7 +4063,7 @@ dependencies = [
"log",
"ndarray",
"num-traits",
- "object_store 0.12.5",
+ "object_store",
"prost",
"prost-build",
"prost-types",
@@ -3784,7 +4075,6 @@ dependencies = [
"serde",
"serde_json",
"smallvec",
- "snafu",
"tempfile",
"tokio",
"tracing",
@@ -3794,9 +4084,9 @@ dependencies = [
[[package]]
name = "lance-io"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef5314703fa8c8baed04193cc669da80ab42521c6319d3cc921a4a997690dcc0"
+checksum = "bab8c98ef1b870b20541d27f3ca4efdf7c9f5c25214233be07d231ba88900219"
dependencies = [
"arrow",
"arrow-arith",
@@ -3820,10 +4110,9 @@ dependencies = [
"lance-arrow",
"lance-core",
"lance-namespace",
- "libc",
"log",
"moka",
- "object_store 0.12.5",
+ "object_store",
"object_store_opendal",
"opendal",
"path_abs",
@@ -3831,7 +4120,6 @@ dependencies = [
"prost",
"rand 0.9.2",
"serde",
- "snafu",
"tempfile",
"tokio",
"tracing",
@@ -3840,9 +4128,9 @@ dependencies = [
[[package]]
name = "lance-linalg"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51aa9b73279f505b2bec0f194c7a2390ca74ad3260131e631a7bef8d97d54b2e"
+checksum = "6b4c51cad0ac780b02dc4da48528479e7693c03e8d05390510bbc69ca2a9a1f1"
dependencies = [
"arrow-array",
"arrow-buffer",
@@ -3858,31 +4146,29 @@ dependencies = [
[[package]]
name = "lance-namespace"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39cd01581f55ce45c49cbe494ee86c7ba7ca4ca3654690fd820941cd9105a46e"
+checksum = "014e8332ca0615506342e0d3af608639864b68396973be14239f09c9f21f1fc2"
dependencies = [
"arrow",
"async-trait",
"bytes",
"lance-core",
"lance-namespace-reqwest-client",
- "serde",
"snafu",
]
[[package]]
name = "lance-namespace-impls"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2cb89f3933060f01350ad05a5a3fbda952e8ba638799bf8ac4cd2368416ee46"
+checksum = "e8d1231906a3cf92dd3dcda7d14a09c4835af6cd2bcd76dfd2481e87f20a282d"
dependencies = [
"arrow",
"arrow-ipc",
"arrow-schema",
"async-trait",
"bytes",
- "chrono",
"futures",
"lance",
"lance-core",
@@ -3892,10 +4178,9 @@ dependencies = [
"lance-namespace",
"lance-table",
"log",
- "object_store 0.12.5",
+ "object_store",
"rand 0.9.2",
"serde_json",
- "snafu",
"tokio",
"url",
]
@@ -3906,7 +4191,7 @@ version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6369eee4682fb11edf538388b43c61ce288b8302fe89bb40944d7daa7faaae99"
dependencies = [
- "reqwest",
+ "reqwest 0.12.28",
"serde",
"serde_json",
"serde_repr",
@@ -3916,9 +4201,9 @@ dependencies = [
[[package]]
name = "lance-table"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db70650465a1af174b7dfe6948ec91a3d466ada12e11274eb66e51132173aa0"
+checksum = "b16f1355904aea4ebb04ffc70c58c97901e10bde44452b4b021de4a1f329250d"
dependencies = [
"arrow",
"arrow-array",
@@ -3936,7 +4221,7 @@ dependencies = [
"lance-file",
"lance-io",
"log",
- "object_store 0.12.5",
+ "object_store",
"prost",
"prost-build",
"prost-types",
@@ -3955,9 +4240,9 @@ dependencies = [
[[package]]
name = "lance-tokenizer"
-version = "6.0.1"
+version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb08ef9382c9d58036c323db2c19cc097e02d1d0d87714fc7176b5d3b36a31aa"
+checksum = "b39b7f5ed9d0c0b716bf599b559d888267ed1dfe4c4e29d3648b51d2a28940cf"
dependencies = [
"rust-stemmers",
"serde",
@@ -4140,7 +4425,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"generator",
"scoped-tls",
"tracing",
@@ -4215,8 +4500,17 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
- "cfg-if",
- "digest",
+ "cfg-if 1.0.4",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "mea"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2640d335e7273dacdcf51044026139b2e269c3bb0dfc3f8cb3496b85e3f6a42c"
+dependencies = [
+ "slab",
]
[[package]]
@@ -4231,7 +4525,7 @@ version = "7.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"miette-derive",
"serde",
"unicode-width 0.1.14",
@@ -4281,16 +4575,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
- "wasi",
+ "wasi 0.11.1+wasi-snapshot-preview1",
"windows-sys 0.61.2",
]
-[[package]]
-name = "mock_instant"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6"
-
[[package]]
name = "moka"
version = "0.12.15"
@@ -4311,6 +4599,12 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "more-asserts"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e"
+
[[package]]
name = "multimap"
version = "0.10.1"
@@ -4362,6 +4656,15 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+[[package]]
+name = "ntapi"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
@@ -4452,6 +4755,34 @@ dependencies = [
"libc",
]
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "objc2-io-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
+dependencies = [
+ "libc",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-system-configuration"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396"
+dependencies = [
+ "objc2-core-foundation",
+]
+
[[package]]
name = "object"
version = "0.37.3"
@@ -4463,16 +4794,18 @@ dependencies = [
[[package]]
name = "object_store"
-version = "0.12.5"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbfbfff40aeccab00ec8a910b57ca8ecf4319b335c542f2edcd19dd25a1e2a00"
+checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49"
dependencies = [
"async-trait",
"base64",
"bytes",
"chrono",
"form_urlencoded",
- "futures",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
"http 1.4.0",
"http-body-util",
"httparse",
@@ -4482,11 +4815,11 @@ dependencies = [
"md-5",
"parking_lot",
"percent-encoding",
- "quick-xml 0.38.4",
- "rand 0.9.2",
- "reqwest",
+ "quick-xml 0.39.4",
+ "rand 0.10.1",
+ "reqwest 0.12.28",
"ring",
- "rustls-pemfile",
+ "rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
@@ -4499,48 +4832,34 @@ dependencies = [
"web-time",
]
-[[package]]
-name = "object_store"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49"
-dependencies = [
- "async-trait",
- "bytes",
- "chrono",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http 1.4.0",
- "humantime",
- "itertools 0.14.0",
- "parking_lot",
- "percent-encoding",
- "thiserror",
- "tokio",
- "tracing",
- "url",
- "walkdir",
- "wasm-bindgen-futures",
- "web-time",
-]
-
[[package]]
name = "object_store_opendal"
-version = "0.55.0"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "113ab0769e972eee585e57407b98de08bda5354fa28e8ba4d89038d6cb6a8991"
+checksum = "08298874eee5935c95bcaa393148834f9c53d904461ca15584a041d8a1c907c2"
dependencies = [
"async-trait",
"bytes",
"chrono",
"futures",
- "object_store 0.12.5",
+ "mea",
+ "object_store",
"opendal",
"pin-project",
"tokio",
]
+[[package]]
+name = "omnigraph-api-types"
+version = "0.7.0"
+dependencies = [
+ "omnigraph-compiler",
+ "omnigraph-engine",
+ "serde",
+ "serde_json",
+ "utoipa",
+]
+
[[package]]
name = "omnigraph-cli"
version = "0.7.0"
@@ -4550,13 +4869,14 @@ dependencies = [
"color-eyre",
"lance",
"lance-index",
+ "omnigraph-api-types",
"omnigraph-cluster",
"omnigraph-compiler",
"omnigraph-engine",
"omnigraph-policy",
"omnigraph-server",
"predicates",
- "reqwest",
+ "reqwest 0.12.28",
"serde",
"serde_json",
"serde_yaml",
@@ -4574,7 +4894,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror",
"time",
@@ -4595,12 +4915,10 @@ dependencies = [
"arrow-select",
"pest",
"pest_derive",
- "reqwest",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"thiserror",
- "tokio",
]
[[package]]
@@ -4627,16 +4945,16 @@ dependencies = [
"lance-namespace",
"lance-namespace-impls",
"lance-table",
- "object_store 0.12.5",
+ "object_store",
"omnigraph-compiler",
"omnigraph-policy",
"proptest",
"regex",
- "reqwest",
+ "reqwest 0.12.28",
"serde",
"serde_json",
"serial_test",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror",
"time",
@@ -4674,6 +4992,7 @@ dependencies = [
"futures",
"lance",
"lance-index",
+ "omnigraph-api-types",
"omnigraph-cluster",
"omnigraph-compiler",
"omnigraph-engine",
@@ -4683,7 +5002,7 @@ dependencies = [
"serde_json",
"serde_yaml",
"serial_test",
- "sha2",
+ "sha2 0.10.9",
"subtle",
"tempfile",
"thiserror",
@@ -4708,33 +5027,226 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
-name = "opendal"
-version = "0.55.0"
+name = "oneshot"
+version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d075ab8a203a6ab4bc1bce0a4b9fe486a72bf8b939037f4b78d95386384bc80a"
+checksum = "269bca4c2591a28585d6bf10d9ed0332b7d76900a1b02bec41bdc3a2cdcda107"
+
+[[package]]
+name = "opendal"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b31d3d8e99a85d83b73ec26647f5607b80578ed9375810b6e44ffa3590a236"
+dependencies = [
+ "ctor",
+ "opendal-core",
+ "opendal-layer-concurrent-limit",
+ "opendal-layer-logging",
+ "opendal-layer-retry",
+ "opendal-layer-timeout",
+ "opendal-service-azblob",
+ "opendal-service-azdls",
+ "opendal-service-gcs",
+ "opendal-service-hf",
+ "opendal-service-oss",
+ "opendal-service-s3",
+]
+
+[[package]]
+name = "opendal-core"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1849dd2687e173e776d3af5fce1ba3ae47b9dd37a09d1c4deba850ef45fe00ca"
dependencies = [
"anyhow",
- "backon",
"base64",
"bytes",
- "crc32c",
"futures",
- "getrandom 0.2.17",
"http 1.4.0",
"http-body 1.0.1",
"jiff",
"log",
"md-5",
+ "mea",
"percent-encoding",
"quick-xml 0.38.4",
- "reqsign",
- "reqwest",
+ "reqsign-core",
+ "reqwest 0.13.4",
"serde",
"serde_json",
- "sha2",
"tokio",
"url",
"uuid",
+ "web-time",
+]
+
+[[package]]
+name = "opendal-layer-concurrent-limit"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "048b1b29c503263bdd80a9afe46a68cd02ea9bd361185b1feab4b151078998e9"
+dependencies = [
+ "futures",
+ "http 1.4.0",
+ "mea",
+ "opendal-core",
+]
+
+[[package]]
+name = "opendal-layer-logging"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2645adc988b12eda106e2679ae529facfbbaa868ceb706f6f8125c6af15c47b"
+dependencies = [
+ "log",
+ "opendal-core",
+]
+
+[[package]]
+name = "opendal-layer-retry"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eac134ffa4ddda6131a640a84a5315996424b9416c85052f8c64c1a33b70ad4"
+dependencies = [
+ "backon",
+ "log",
+ "opendal-core",
+]
+
+[[package]]
+name = "opendal-layer-timeout"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "619586ab7480c2e3009f6d18eabab18957bc094778fd130bcc38924970a90f4c"
+dependencies = [
+ "opendal-core",
+ "tokio",
+]
+
+[[package]]
+name = "opendal-service-azblob"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7452bf3ec61cfd81ac9ad9ada17825931e9e371d44a045c6bfab9596c0a2ac3b"
+dependencies = [
+ "base64",
+ "bytes",
+ "http 1.4.0",
+ "log",
+ "opendal-core",
+ "opendal-service-azure-common",
+ "quick-xml 0.38.4",
+ "reqsign-azure-storage",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "serde",
+ "sha2 0.10.9",
+ "uuid",
+]
+
+[[package]]
+name = "opendal-service-azdls"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f9884c2d8cf8ba2bb077d79c877dac5863ba3bab9e2c9c1e41a2e0491404772"
+dependencies = [
+ "bytes",
+ "http 1.4.0",
+ "log",
+ "opendal-core",
+ "opendal-service-azure-common",
+ "quick-xml 0.38.4",
+ "reqsign-azure-storage",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "opendal-service-azure-common"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb0e45d6c8dcf66ce2da20e241bcb80e6e540e109a4ff20f318f6c9b4c54e0c"
+dependencies = [
+ "http 1.4.0",
+ "opendal-core",
+]
+
+[[package]]
+name = "opendal-service-gcs"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a49477a10163431896d106136117f5670717f9c9e49cf6f710528800c6633a"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http 1.4.0",
+ "log",
+ "opendal-core",
+ "percent-encoding",
+ "quick-xml 0.38.4",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "reqsign-google",
+ "serde",
+ "serde_json",
+ "tokio",
+]
+
+[[package]]
+name = "opendal-service-hf"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b2ab7a2a8a11dfe257ef4db5c0de798acbcd0d6429c37382dad2154bc06a388"
+dependencies = [
+ "bytes",
+ "hf-xet",
+ "http 1.4.0",
+ "log",
+ "opendal-core",
+ "percent-encoding",
+ "reqwest 0.13.4",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "opendal-service-oss"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c8a917829ad06d21b639558532cb0101fe49b040d946d673a73018683fac05"
+dependencies = [
+ "bytes",
+ "http 1.4.0",
+ "log",
+ "opendal-core",
+ "quick-xml 0.38.4",
+ "reqsign-aliyun-oss",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "serde",
+]
+
+[[package]]
+name = "opendal-service-s3"
+version = "0.56.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dadddeb9bb50b0d30927dd914c298c4ddca47e4c1cfa7674d311f0cf9b051c8"
+dependencies = [
+ "base64",
+ "bytes",
+ "crc32c",
+ "http 1.4.0",
+ "log",
+ "md-5",
+ "opendal-core",
+ "quick-xml 0.38.4",
+ "reqsign-aws-v4",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "serde",
+ "url",
]
[[package]]
@@ -4768,6 +5280,15 @@ dependencies = [
"hashbrown 0.14.5",
]
+[[package]]
+name = "os_str_bytes"
+version = "6.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "outref"
version = "0.5.2"
@@ -4802,7 +5323,7 @@ version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"libc",
"redox_syscall",
"smallvec",
@@ -4833,8 +5354,8 @@ version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
- "digest",
- "hmac",
+ "digest 0.10.7",
+ "hmac 0.12.1",
]
[[package]]
@@ -4908,7 +5429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220"
dependencies = [
"pest",
- "sha2",
+ "sha2 0.10.9",
]
[[package]]
@@ -5020,7 +5541,7 @@ dependencies = [
"der",
"pbkdf2",
"scrypt",
- "sha2",
+ "sha2 0.10.9",
"spki",
]
@@ -5235,9 +5756,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-xml"
-version = "0.37.5"
+version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
dependencies = [
"memchr",
"serde",
@@ -5245,9 +5766,19 @@ dependencies = [
[[package]]
name = "quick-xml"
-version = "0.38.4"
+version = "0.39.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
+checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2474bd2e5029e7ccb6abb2ba48cf2383a333851dedf495901544281590c7da7f"
dependencies = [
"memchr",
"serde",
@@ -5279,6 +5810,7 @@ version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
+ "aws-lc-rs",
"bytes",
"getrandom 0.3.4",
"lru-slab",
@@ -5310,9 +5842,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.44"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
@@ -5356,6 +5888,17 @@ dependencies = [
"rand_core 0.9.5",
]
+[[package]]
+name = "rand"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.2",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "rand_chacha"
version = "0.3.1"
@@ -5394,6 +5937,12 @@ dependencies = [
"getrandom 0.3.4",
]
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
[[package]]
name = "rand_distr"
version = "0.5.1"
@@ -5467,6 +6016,15 @@ dependencies = [
"crossbeam-utils",
]
+[[package]]
+name = "redb"
+version = "3.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ba239c1c1693315d3cc0e601db3b3965543afbf48c41730fdca2f069f510f4a"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -5543,34 +6101,116 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
-name = "reqsign"
-version = "0.16.5"
+name = "reqsign-aliyun-oss"
+version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43451dbf3590a7590684c25fb8d12ecdcc90ed3ac123433e500447c7d77ed701"
+checksum = "372266b4733756738eeb199a98188037d27a0989980e2600ae7ce1faf00a867d"
dependencies = [
"anyhow",
- "async-trait",
- "base64",
- "chrono",
"form_urlencoded",
- "getrandom 0.2.17",
- "hex",
- "hmac",
- "home",
"http 1.4.0",
- "jsonwebtoken",
"log",
- "once_cell",
"percent-encoding",
- "quick-xml 0.37.5",
- "rand 0.8.5",
- "reqwest",
- "rsa",
+ "reqsign-core",
"rust-ini",
"serde",
"serde_json",
+]
+
+[[package]]
+name = "reqsign-aws-v4"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b75624bd8a466e37ddc0a7b6c33ac859a85347c153a916e1dd9d0b68338f74a"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "form_urlencoded",
+ "hex",
+ "http 1.4.0",
+ "log",
+ "percent-encoding",
+ "quick-xml 0.40.1",
+ "reqsign-core",
+ "rust-ini",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
"sha1",
- "sha2",
+]
+
+[[package]]
+name = "reqsign-azure-storage"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b96928e73ad984de1d99e382749d09e5dab7dd707b767974f7e40aa926b82f"
+dependencies = [
+ "anyhow",
+ "base64",
+ "bytes",
+ "form_urlencoded",
+ "http 1.4.0",
+ "log",
+ "pem",
+ "percent-encoding",
+ "reqsign-core",
+ "rsa",
+ "serde",
+ "serde_json",
+ "sha1",
+]
+
+[[package]]
+name = "reqsign-core"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5fa5cb48808693614d1701fcd3db0b30fa292e0f18e122ae068b6d32eaeed3f"
+dependencies = [
+ "anyhow",
+ "base64",
+ "bytes",
+ "form_urlencoded",
+ "futures",
+ "hex",
+ "hmac 0.13.0",
+ "http 1.4.0",
+ "jiff",
+ "log",
+ "percent-encoding",
+ "rsa",
+ "serde",
+ "serde_json",
+ "sha1",
+ "sha2 0.11.0",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "reqsign-file-read-tokio"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a4b6f3a3fd29ffcc99a90aec585a65217783badfd73acddf847b63ae683bda9"
+dependencies = [
+ "anyhow",
+ "reqsign-core",
+ "tokio",
+]
+
+[[package]]
+name = "reqsign-google"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb215d0876a18b6bd9cdd380b589e5292aaa638ca15266de794b1122d898b6b2"
+dependencies = [
+ "form_urlencoded",
+ "http 1.4.0",
+ "log",
+ "percent-encoding",
+ "reqsign-aws-v4",
+ "reqsign-core",
+ "rsa",
+ "serde",
+ "serde_json",
"tokio",
]
@@ -5616,11 +6256,65 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
- "wasm-streams",
+ "wasm-streams 0.4.2",
"web-sys",
"webpki-roots",
]
+[[package]]
+name = "reqwest"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http 1.4.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.8.1",
+ "hyper-rustls 0.27.7",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls 0.23.36",
+ "rustls-pki-types",
+ "rustls-platform-verifier",
+ "serde",
+ "serde_json",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls 0.26.4",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams 0.5.0",
+ "web-sys",
+]
+
+[[package]]
+name = "reqwest-middleware"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc3f1384cffa4f274dad2d4ddd73aed32fed8f786d96c6be8aa4e5fd3c3b58"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "http 1.4.0",
+ "reqwest 0.13.4",
+ "thiserror",
+ "tower-service",
+]
+
[[package]]
name = "ring"
version = "0.17.14"
@@ -5628,7 +6322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
- "cfg-if",
+ "cfg-if 1.0.4",
"getrandom 0.2.17",
"libc",
"untrusted",
@@ -5637,9 +6331,9 @@ dependencies = [
[[package]]
name = "roaring"
-version = "0.11.3"
+version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885"
+checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9"
dependencies = [
"bytemuck",
"byteorder",
@@ -5651,15 +6345,15 @@ version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
- "const-oid",
- "digest",
+ "const-oid 0.9.6",
+ "digest 0.10.7",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4",
- "sha2",
+ "sha2 0.10.9",
"signature",
"spki",
"subtle",
@@ -5672,7 +6366,7 @@ version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"ordered-multimap",
]
@@ -5765,15 +6459,6 @@ dependencies = [
"security-framework",
]
-[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
-dependencies = [
- "rustls-pki-types",
-]
-
[[package]]
name = "rustls-pki-types"
version = "1.14.0"
@@ -5784,6 +6469,33 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "rustls-platform-verifier"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
+dependencies = [
+ "core-foundation 0.10.1",
+ "core-foundation-sys",
+ "jni",
+ "log",
+ "once_cell",
+ "rustls 0.23.36",
+ "rustls-native-certs",
+ "rustls-platform-verifier-android",
+ "rustls-webpki 0.103.9",
+ "security-framework",
+ "security-framework-sys",
+ "webpki-root-certs",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls-platform-verifier-android"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
+
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -5830,6 +6542,12 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+[[package]]
+name = "safe-transmute"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3944826ff8fa8093089aba3acb4ef44b9446a99a16f3bf4e74af3f77d340ab7d"
+
[[package]]
name = "salsa20"
version = "0.10.2"
@@ -5910,7 +6628,7 @@ checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
dependencies = [
"pbkdf2",
"salsa20",
- "sha2",
+ "sha2 0.10.9",
]
[[package]]
@@ -5936,7 +6654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags",
- "core-foundation",
+ "core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
@@ -6114,13 +6832,13 @@ dependencies = [
[[package]]
name = "sha1"
-version = "0.10.6"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
+ "cfg-if 1.0.4",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
]
[[package]]
@@ -6129,9 +6847,30 @@ version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
+ "cfg-if 1.0.4",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+ "sha2-asm",
+]
+
+[[package]]
+name = "sha2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
+dependencies = [
+ "cfg-if 1.0.4",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
+]
+
+[[package]]
+name = "sha2-asm"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b845214d6175804686b2bd482bcffe96651bb2d1200742b712003504a2dac1ab"
+dependencies = [
+ "cc",
]
[[package]]
@@ -6140,7 +6879,7 @@ version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
dependencies = [
- "digest",
+ "digest 0.10.7",
"keccak",
]
@@ -6153,6 +6892,17 @@ dependencies = [
"lazy_static",
]
+[[package]]
+name = "shellexpand"
+version = "3.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
+dependencies = [
+ "bstr",
+ "dirs",
+ "os_str_bytes",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -6175,7 +6925,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
- "digest",
+ "digest 0.10.7",
"rand_core 0.6.4",
]
@@ -6185,24 +6935,22 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
+[[package]]
+name = "simd_cesu8"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
-[[package]]
-name = "simple_asn1"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
-dependencies = [
- "num-bigint",
- "num-traits",
- "thiserror",
- "time",
-]
-
[[package]]
name = "siphasher"
version = "1.0.2"
@@ -6322,12 +7070,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
dependencies = [
"cc",
- "cfg-if",
+ "cfg-if 1.0.4",
"libc",
"psm",
"windows-sys 0.59.0",
]
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "statrs"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e"
+dependencies = [
+ "approx",
+ "num-traits",
+]
+
[[package]]
name = "std_prelude"
version = "0.2.12"
@@ -6386,6 +7150,12 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+[[package]]
+name = "symlink"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a"
+
[[package]]
name = "syn"
version = "1.0.109"
@@ -6428,6 +7198,41 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "sysinfo"
+version = "0.38.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f"
+dependencies = [
+ "libc",
+ "memchr",
+ "ntapi",
+ "objc2-core-foundation",
+ "objc2-io-kit",
+ "windows",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
+dependencies = [
+ "bitflags",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "tagptr"
version = "0.2.0"
@@ -6503,7 +7308,7 @@ version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
]
[[package]]
@@ -6599,6 +7404,17 @@ dependencies = [
"syn 2.0.117",
]
+[[package]]
+name = "tokio-retry"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a129d95275ebf4c493ec53bf0f8cd95f5ac161bc4f381700809a54f595d4470"
+dependencies = [
+ "pin-project-lite",
+ "rand 0.10.1",
+ "tokio",
+]
+
[[package]]
name = "tokio-rustls"
version = "0.24.1"
@@ -6708,6 +7524,19 @@ dependencies = [
"tracing-core",
]
+[[package]]
+name = "tracing-appender"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c"
+dependencies = [
+ "crossbeam-channel",
+ "symlink",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
[[package]]
name = "tracing-attributes"
version = "0.1.31"
@@ -6750,6 +7579,16 @@ dependencies = [
"tracing-core",
]
+[[package]]
+name = "tracing-serde"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
[[package]]
name = "tracing-subscriber"
version = "0.3.22"
@@ -6760,12 +7599,15 @@ dependencies = [
"nu-ansi-term",
"once_cell",
"regex-automata",
+ "serde",
+ "serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
+ "tracing-serde",
]
[[package]]
@@ -6791,9 +7633,15 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "typenum"
-version = "1.19.0"
+version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
+name = "typewit"
+version = "1.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "214ca0b2191785cbc06209b9ca1861e048e39b5ba33574b3cedd58363d5bb5f6"
[[package]]
name = "ucd-trie"
@@ -7014,6 +7862,15 @@ version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+[[package]]
+name = "wasi"
+version = "0.14.7+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
+dependencies = [
+ "wasip2",
+]
+
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
@@ -7032,13 +7889,22 @@ dependencies = [
"wit-bindgen",
]
+[[package]]
+name = "wasite"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66fe902b4a6b8028a753d5424909b764ccf79b7a209eac9bf97e59cda9f71a42"
+dependencies = [
+ "wasi 0.14.7+wasi-0.2.4",
+]
+
[[package]]
name = "wasm-bindgen"
version = "0.2.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
@@ -7051,7 +7917,7 @@ version = "0.4.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f"
dependencies = [
- "cfg-if",
+ "cfg-if 1.0.4",
"futures-util",
"js-sys",
"once_cell",
@@ -7126,6 +7992,19 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "wasm-streams"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
[[package]]
name = "wasmparser"
version = "0.244.0"
@@ -7158,6 +8037,15 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "webpki-roots"
version = "1.0.6"
@@ -7167,6 +8055,35 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "whoami"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6a5b12f9df4f978d2cfdb1bd3bac52433f44393342d7ee9c25f5a1c14c0f45d"
+dependencies = [
+ "libc",
+ "libredox",
+ "objc2-system-configuration",
+ "wasite",
+ "web-sys",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -7176,6 +8093,33 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections",
+ "windows-core",
+ "windows-future",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core",
+]
+
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -7189,6 +8133,17 @@ dependencies = [
"windows-strings",
]
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core",
+ "windows-link",
+ "windows-threading",
+]
+
[[package]]
name = "windows-implement"
version = "0.60.2"
@@ -7217,6 +8172,27 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core",
+ "windows-link",
+]
+
+[[package]]
+name = "windows-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
+dependencies = [
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
[[package]]
name = "windows-result"
version = "0.4.1"
@@ -7304,6 +8280,15 @@ dependencies = [
"windows_x86_64_msvc 0.53.1",
]
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link",
+]
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -7503,6 +8488,153 @@ dependencies = [
"tap",
]
+[[package]]
+name = "xet-client"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e1e496dcbe6a09017acdfaf48e1a646735e7ff5b2a49e2c7e081cca77a59bc8"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "base64",
+ "bytes",
+ "clap",
+ "crc32fast",
+ "futures",
+ "http 1.4.0",
+ "hyper 1.8.1",
+ "lazy_static",
+ "more-asserts",
+ "rand 0.10.1",
+ "redb",
+ "reqwest 0.13.4",
+ "reqwest-middleware",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "statrs",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-retry",
+ "tracing",
+ "tracing-subscriber",
+ "url",
+ "urlencoding",
+ "web-time",
+ "xet-core-structures",
+ "xet-runtime",
+]
+
+[[package]]
+name = "xet-core-structures"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb838aa8eb67d730af301584cf003caad407487606058292a6750711b603fbee"
+dependencies = [
+ "async-trait",
+ "base64",
+ "blake3",
+ "bytemuck",
+ "bytes",
+ "clap",
+ "countio",
+ "csv",
+ "futures",
+ "futures-util",
+ "getrandom 0.4.2",
+ "heapify",
+ "itertools 0.14.0",
+ "lazy_static",
+ "lz4_flex",
+ "more-asserts",
+ "rand 0.10.1",
+ "regex",
+ "safe-transmute",
+ "serde",
+ "static_assertions",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "uuid",
+ "web-time",
+ "xet-runtime",
+]
+
+[[package]]
+name = "xet-data"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67fd409bef621411a9d9013798540bb8036cb2678f03ab39af89a5e88034ed8c"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "bytes",
+ "chrono",
+ "clap",
+ "gearhash",
+ "http 1.4.0",
+ "itertools 0.14.0",
+ "lazy_static",
+ "more-asserts",
+ "rand 0.10.1",
+ "serde",
+ "serde_json",
+ "sha2 0.10.9",
+ "tempfile",
+ "thiserror",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "url",
+ "uuid",
+ "walkdir",
+ "xet-client",
+ "xet-core-structures",
+ "xet-runtime",
+]
+
+[[package]]
+name = "xet-runtime"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15d8f121c33866f7648b737abe70d0e2dd9c0af4ffdd7219207531d0283aa63d"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "bytes",
+ "chrono",
+ "colored",
+ "const-str",
+ "ctor",
+ "dirs",
+ "futures",
+ "git-version",
+ "humantime",
+ "konst",
+ "lazy_static",
+ "libc",
+ "more-asserts",
+ "oneshot",
+ "pin-project",
+ "rand 0.10.1",
+ "reqwest 0.13.4",
+ "serde",
+ "serde_json",
+ "shellexpand",
+ "sysinfo",
+ "thiserror",
+ "tokio",
+ "tokio-util",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+ "whoami",
+ "winapi",
+]
+
[[package]]
name = "xmlparser"
version = "0.13.6"
diff --git a/Cargo.toml b/Cargo.toml
index 918ac05..c442242 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ members = [
"crates/omnigraph-compiler",
"crates/omnigraph",
"crates/omnigraph-cli",
+ "crates/omnigraph-api-types",
"crates/omnigraph-cluster",
"crates/omnigraph-policy",
"crates/omnigraph-server",
@@ -30,14 +31,14 @@ datafusion-common = "53"
datafusion-expr = "53"
datafusion-functions-aggregate = "53"
-lance = { version = "6.0.1", default-features = false, features = ["aws"] }
-lance-datafusion = "6.0.1"
-lance-file = "6.0.1"
-lance-index = "6.0.1"
-lance-linalg = "6.0.1"
-lance-namespace = "6.0.1"
-lance-namespace-impls = "6.0.1"
-lance-table = "6.0.1"
+lance = { version = "7.0.0", default-features = false, features = ["aws"] }
+lance-datafusion = "7.0.0"
+lance-file = "7.0.0"
+lance-index = "7.0.0"
+lance-linalg = "7.0.0"
+lance-namespace = "7.0.0"
+lance-namespace-impls = "7.0.0"
+lance-table = "7.0.0"
ulid = "1"
futures = "0.3"
@@ -47,7 +48,7 @@ pest = "2"
pest_derive = "2"
thiserror = "2"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "net", "signal", "sync"] }
-clap = { version = "4", features = ["derive"] }
+clap = { version = "4.6", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
@@ -63,7 +64,7 @@ base64 = "0.22"
ariadne = "0.4"
regex = "1"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
-object_store = { version = "0.12.5", default-features = false, features = ["aws", "fs"] }
+object_store = { version = "0.13.2", default-features = false, features = ["aws", "fs"] }
fail = "0.5"
time = { version = "0.3", features = ["formatting"] }
axum = { version = "0.8", features = ["json", "macros"] }
diff --git a/README.md b/README.md
index a75a839..9c4f8bc 100644
--- a/README.md
+++ b/README.md
@@ -3,15 +3,15 @@
[](LICENSE)
[](rust-toolchain.toml)
[](https://crates.io/crates/omnigraph-cli)
-[](https://github.com/ModernRelay/omnigraph/actions/workflows/ci.yml)
**Lakehouse native graph engine built for context assembly**
-Omnigraph acts as operational state & coordination layer for agents
+Omnigraph acts as operational state & coordination layer for agents.
+Hundreds of agents can enrich the graph on parallel isolated branches and changes can be reviewed and merged safely.
- Git-style versioning & branching
- Multimodal retrieval (graph+vector/fts+filters) optimized for context assembly
-- Object storage native (S3, RustFS)
+- Runs on the local filesystem or any S3-compatible object store (AWS S3, R2, MinIO, RustFS)
- Native blob-as-data support (docs, images, videos, etc)
- VPC, On-prem, hybrid deployment
- [`Lance`](https://github.com/lance-format/lance) format as open storage layer
@@ -51,62 +51,138 @@ brew tap ModernRelay/tap
brew install ModernRelay/tap/omnigraph
```
-For starter graphs and agent skills to bootstrap and operate Omnigraph, see [`ModernRelay/omnigraph-cookbooks`](https://github.com/ModernRelay/omnigraph-cookbooks).
+## Quick start
-## One-Command Local RustFS Bootstrap
+The fastest path is an **embedded, local file-backed graph** — no server, no
+object store, no Docker:
```bash
-curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph/main/scripts/local-rustfs-bootstrap.sh | bash
+# A schema and one row of data
+cat > schema.pg <<'PG'
+node Person {
+ slug: String @key
+ name: String
+ title: String?
+}
+PG
+echo '{"type":"Person","data":{"slug":"alice","name":"Alice","title":"Engineer"}}' > people.jsonl
+
+# Create → load (--mode is required) → query
+omnigraph init --schema schema.pg ./graph.omni
+omnigraph load --data people.jsonl --mode overwrite --store ./graph.omni
+omnigraph query find_people --store ./graph.omni --params '{"t":"Engineer"}' \
+ -e 'query find_people($t: String) { match { $p: Person { title: $t } } return { $p.name } }'
+
+# Branch, write in isolation, merge — Git-style across the whole graph
+omnigraph branch create --from main review/new-hires --store ./graph.omni
+omnigraph branch merge review/new-hires --into main --store ./graph.omni
```
-That bootstrap:
+**Storage backends** — the same flow runs on any backend; only the graph address changes:
-- starts RustFS on `127.0.0.1:9000`
-- creates a bucket and S3-backed graph
-- loads the checked-in context fixture
-- launches `omnigraph-server` on `127.0.0.1:8080`
+| Backend | Use it for | Graph address |
+|---|---|---|
+| **Embedded** (local filesystem) | dev, demos, single machine — the default | `./graph.omni` |
+| **Object storage** (AWS S3, R2, GCS-S3) | shared, multi-host, durable | `s3://bucket/graph.omni` (+ the `AWS_*` env) |
+| **RustFS / MinIO** | rehearse the S3 path locally, no cloud account | `s3://…` against a local endpoint → [deployment guide](docs/user/deployment.md#testing-against-s3-locally) |
-Docker must be installed and running first.
+`init` takes the address as its positional argument (`omnigraph init --schema schema.pg `); `load`, `query`, and `branch` take it via `--store `.
-The RustFS bootstrap prefers the rolling `edge` binaries and only falls back to
-source builds when release assets are unavailable.
+For a **served, multi-graph deployment** (the cluster model), see [Common Commands](#common-commands) below.
-If a previous run left objects under the same graph prefix but did not finish
-initializing the graph, rerun with `RESET_REPO=1` or set `PREFIX` to a new
-value.
+## Set it up with an AI agent
+
+Omnigraph is built to be set up by coding agents. Paste this into Claude Code,
+Cursor, or any agent that can read a URL, install a package, and run a shell
+command — it installs the skill, reads the docs, and walks you through setup for
+your use case:
+
+```text
+Help me set up Omnigraph (a lakehouse-native graph engine for agents).
+
+1. Install the Omnigraph skill so you operate it correctly:
+ npx skills add ModernRelay/omnigraph@omnigraph
+2. Read the docs at https://github.com/ModernRelay/omnigraph — start with
+ docs/user/quickstart.md, then docs/user/clusters/index.md.
+3. Skim the starter graphs and seed data in the cookbooks:
+ https://github.com/ModernRelay/omnigraph-cookbooks
+4. Ask me what I want to build (company brain, agent memory, dev graph,
+ research / R&D layer, …). Then install the CLI, stand up a first graph for
+ that use case, load a little data, and run a query so I can see it working.
+```
+
+Works with any agent that can browse a URL, install a package, and run a shell.
+
+## Agent skill & starter graphs
+
+This repo ships the [**`omnigraph` agent skill**](skills/omnigraph) — the
+operational playbook (cluster mode, the two config surfaces, schema evolution,
+query linting, data writes, branches, Cedar policy, and common gotchas) that
+teaches a coding agent to drive Omnigraph correctly. Install it with:
+
+```bash
+npx skills add ModernRelay/omnigraph@omnigraph
+```
+
+For ready-to-run graphs with real seed data (company brain, VC operating system,
+pharma & industry intel),
+[`ModernRelay/omnigraph-cookbooks`](https://github.com/ModernRelay/omnigraph-cookbooks)
+is the fastest way to see Omnigraph shaped to a real domain. To rehearse the S3
+path locally, see [deployment.md → Testing against S3 locally](docs/user/deployment.md#testing-against-s3-locally).
## Common Commands
-The same URI works for local paths, `s3://…`, or `http://host:port`.
+A deployment is a **cluster**. A `cluster.yaml` declares its graphs, schemas,
+stored queries, and policies; you converge it with `cluster apply` and serve it.
+The server is cluster-first — it boots only from a cluster and serves every graph
+under `/graphs/{id}/…`. Day-to-day work goes through that server: graphs are
+addressed with `--server ` (+ `--graph `), and `query`/`mutate`
+invoke a stored query from the catalog **by name**.
```bash
-omnigraph init --schema ./schema.pg ./graph.omni
-omnigraph load --data ./data.jsonl ./graph.omni
-omnigraph read --query ./queries.gq --name get_person --params '{"name":"Alice"}' ./graph.omni
-omnigraph change --query ./queries.gq --name insert_person --params '{"name":"Mina"}' ./graph.omni
-omnigraph branch create --from main feature-x ./graph.omni
-omnigraph branch merge feature-x --into main ./graph.omni
+# 1. Converge the declared cluster, then serve it (--as attributes the apply)
+omnigraph cluster apply --config ./company-brain --as you
+omnigraph-server --cluster ./company-brain --bind 0.0.0.0:8080
+# or config-free from object storage — the bucket IS the deployment:
+# omnigraph-server --cluster s3://my-bucket/company-brain --bind 0.0.0.0:8080
+
+# 2. Work against the served graph — stored queries invoked by name
+omnigraph query find_people --server prod --graph knowledge --params '{"q":"AI safety"}'
+omnigraph mutate add_person --server prod --graph knowledge --params '{"name":"Mina"}'
+omnigraph load --data ./data.jsonl --mode merge --server prod --graph knowledge
+
+# 3. Branch and merge, Git-style across the whole graph
+omnigraph branch create --from main review/2026-06 --server prod --graph knowledge
+omnigraph branch merge review/2026-06 --into main --server prod --graph knowledge
```
-See [docs/user/cli.md](docs/user/cli.md) for schema apply, snapshots, data loading, commits, and policy commands.
+Set a default scope (or a `--profile`) in `~/.omnigraph/config.yaml` — operator
+identity, named servers/clusters, credentials — and the `--server`/`--graph`
+flags drop away (`omnigraph query find_people --params …`).
+
+**Local / ad-hoc.** For quick iteration on a standalone graph (no cluster, no
+server), address storage directly with `--store` (or a positional `file://` /
+`s3://` URI) and run ad-hoc `.gq` with `--query` (the positional then selects
+which query in the file):
+
+```bash
+omnigraph init --schema ./schema.pg ./graph.omni
+omnigraph load --data ./data.jsonl --mode merge --store ./graph.omni
+omnigraph query --query ./queries.gq get_person --params '{"name":"Alice"}' --store ./graph.omni
+```
+
+See [docs/user/cli/index.md](docs/user/cli/index.md), the
+[CLI reference](docs/user/cli/reference.md), the
+[cluster guide](docs/user/clusters/index.md), and the
+[deployment guide](docs/user/deployment.md) for schema apply, snapshots, commits,
+profiles, and policy/queries tooling.
## Clients
For programmatic access to a running `omnigraph-server`:
-- **TypeScript SDK** — [`@modernrelay/omnigraph`](https://www.npmjs.com/package/@modernrelay/omnigraph) ([source](https://github.com/ModernRelay/omnigraph-ts/tree/main/packages/sdk)). Instance-per-client, typed errors, camelCase types, async-iterator streaming export.
-
- ```bash
- npm install @modernrelay/omnigraph
- ```
-
-- **Model Context Protocol server** — [`@modernrelay/omnigraph-mcp`](https://www.npmjs.com/package/@modernrelay/omnigraph-mcp) ([source](https://github.com/ModernRelay/omnigraph-ts/tree/main/packages/mcp)). Bridges Omnigraph to LLM hosts (Claude Desktop, Claude Code, …) over stdio. Exposes tools and resources for schema, branches, queries, mutations, ingest, and bundles curated best-practices guidance from the cookbook.
-
- ```bash
- npm install -g @modernrelay/omnigraph-mcp
- ```
-
-Both packages are versioned in lockstep with `omnigraph-server` on major.minor: `@modernrelay/omnigraph@X.Y.*` targets `omnigraph-server@X.Y.*`. See [`ModernRelay/omnigraph-ts`](https://github.com/ModernRelay/omnigraph-ts) for the monorepo.
+- **TypeScript SDK + MCP server** — [`@modernrelay/omnigraph`](https://www.npmjs.com/package/@modernrelay/omnigraph) and [`@modernrelay/omnigraph-mcp`](https://www.npmjs.com/package/@modernrelay/omnigraph-mcp), versioned in lockstep with `omnigraph-server`. Source, docs, and examples: [`ModernRelay/omnigraph-ts`](https://github.com/ModernRelay/omnigraph-ts).
+- **Python SDK** — coming soon.
## Docs
@@ -130,10 +206,13 @@ Notes:
## Workspace Crates
-- `crates/omnigraph-compiler`: shared schema/query parser, typechecker, catalog, and IR lowering
-- `crates/omnigraph`: storage/runtime, branching, merge, change detection, and query execution
-- `crates/omnigraph-cli`: CLI for graph lifecycle (init/load), query/mutate, branch/commit/merge, schema/lint, snapshot/export, policy, and maintenance (optimize/cleanup)
-- `crates/omnigraph-server`: Axum HTTP server for remote reads, changes, ingest, export, branches, and commits
+- `crates/omnigraph-compiler`: shared schema/query parser, typechecker, catalog, and IR lowering (zero Lance dependency)
+- `crates/omnigraph` (package `omnigraph-engine`): storage/runtime, branching, merge, change detection, query execution, and embeddings
+- `crates/omnigraph-policy`: Cedar policy compilation and enforcement
+- `crates/omnigraph-api-types`: shared HTTP wire DTOs used by both the server and the CLI
+- `crates/omnigraph-cluster`: cluster config validation, planning, and apply (the control plane)
+- `crates/omnigraph-server`: Axum HTTP server — cluster-first, serving N graphs under `/graphs/{id}/…`
+- `crates/omnigraph-cli`: CLI for graph lifecycle (init/load), query/mutate, branch/commit/merge, schema/lint, snapshot/export, cluster control, policy/queries, profiles, and maintenance (optimize/repair/cleanup)
## Contributing
diff --git a/crates/omnigraph-api-types/Cargo.toml b/crates/omnigraph-api-types/Cargo.toml
new file mode 100644
index 0000000..d69d4fe
--- /dev/null
+++ b/crates/omnigraph-api-types/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "omnigraph-api-types"
+version = "0.7.0"
+edition = "2024"
+description = "Shared HTTP wire DTOs for Omnigraph — request/response types and engine-result → DTO mappings used by both omnigraph-server and omnigraph-cli (RFC-009). Plain serde/utoipa types; no transport or server internals."
+license = "MIT"
+repository = "https://github.com/ModernRelay/omnigraph"
+homepage = "https://github.com/ModernRelay/omnigraph"
+documentation = "https://docs.rs/omnigraph-api-types"
+
+[dependencies]
+omnigraph = { package = "omnigraph-engine", path = "../omnigraph", version = "0.7.0" }
+omnigraph-compiler = { path = "../omnigraph-compiler", version = "0.7.0" }
+serde = { workspace = true }
+serde_json = { workspace = true }
+utoipa = { workspace = true }
diff --git a/crates/omnigraph-api-types/src/lib.rs b/crates/omnigraph-api-types/src/lib.rs
new file mode 100644
index 0000000..2814602
--- /dev/null
+++ b/crates/omnigraph-api-types/src/lib.rs
@@ -0,0 +1,704 @@
+//! Shared HTTP wire DTOs (RFC-009 Phase 2) — moved from
+//! omnigraph-server's api module so server and CLI share one definition
+//! and one engine-result -> DTO mapping per verb. Plain serde/utoipa
+//! types; no transport, no server internals.
+
+use omnigraph::db::{GraphCommit, MergeOutcome, ReadTarget, SchemaApplyResult, Snapshot};
+use omnigraph::error::{MergeConflict, MergeConflictKind};
+use omnigraph::loader::{LoadMode, LoadResult};
+use omnigraph_compiler::SchemaMigrationStep;
+use omnigraph_compiler::query::ast::Param;
+use omnigraph_compiler::result::QueryResult;
+use omnigraph_compiler::types::{PropType, ScalarType};
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+use utoipa::{IntoParams, ToSchema};
+
+/// Shadow enum for documenting [`LoadMode`] in the OpenAPI schema.
+#[derive(ToSchema)]
+#[schema(as = LoadMode)]
+#[allow(dead_code)]
+enum LoadModeSchema {
+ /// Overwrite existing data.
+ #[schema(rename = "overwrite")]
+ Overwrite,
+ /// Append to existing data.
+ #[schema(rename = "append")]
+ Append,
+ /// Merge by id key (upsert).
+ #[schema(rename = "merge")]
+ Merge,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct SnapshotTableOutput {
+ pub table_key: String,
+ pub table_path: String,
+ pub table_version: u64,
+ pub table_branch: Option,
+ pub row_count: u64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct SnapshotOutput {
+ pub branch: String,
+ pub manifest_version: u64,
+ pub tables: Vec,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchCreateRequest {
+ /// Parent branch to fork from. Defaults to `main`.
+ pub from: Option,
+ /// Name of the new branch. Must not already exist.
+ pub name: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchCreateOutput {
+ pub uri: String,
+ pub from: String,
+ pub name: String,
+ pub actor_id: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchListOutput {
+ pub branches: Vec,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchDeleteOutput {
+ pub uri: String,
+ pub name: String,
+ pub actor_id: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchMergeRequest {
+ /// Source branch whose commits will be merged.
+ pub source: String,
+ /// Target branch that will receive the merge. Defaults to `main`.
+ pub target: Option,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum BranchMergeOutcome {
+ AlreadyUpToDate,
+ FastForward,
+ Merged,
+}
+
+impl From for BranchMergeOutcome {
+ fn from(value: MergeOutcome) -> Self {
+ match value {
+ MergeOutcome::AlreadyUpToDate => Self::AlreadyUpToDate,
+ MergeOutcome::FastForward => Self::FastForward,
+ MergeOutcome::Merged => Self::Merged,
+ }
+ }
+}
+
+impl BranchMergeOutcome {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ Self::AlreadyUpToDate => "already_up_to_date",
+ Self::FastForward => "fast_forward",
+ Self::Merged => "merged",
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct BranchMergeOutput {
+ pub source: String,
+ pub target: String,
+ pub outcome: BranchMergeOutcome,
+ pub actor_id: Option,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum MergeConflictKindOutput {
+ DivergentInsert,
+ DivergentUpdate,
+ DeleteVsUpdate,
+ OrphanEdge,
+ UniqueViolation,
+ CardinalityViolation,
+ ValueConstraintViolation,
+}
+
+impl MergeConflictKindOutput {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ Self::DivergentInsert => "divergent_insert",
+ Self::DivergentUpdate => "divergent_update",
+ Self::DeleteVsUpdate => "delete_vs_update",
+ Self::OrphanEdge => "orphan_edge",
+ Self::UniqueViolation => "unique_violation",
+ Self::CardinalityViolation => "cardinality_violation",
+ Self::ValueConstraintViolation => "value_constraint_violation",
+ }
+ }
+}
+
+impl From for MergeConflictKindOutput {
+ fn from(value: MergeConflictKind) -> Self {
+ match value {
+ MergeConflictKind::DivergentInsert => Self::DivergentInsert,
+ MergeConflictKind::DivergentUpdate => Self::DivergentUpdate,
+ MergeConflictKind::DeleteVsUpdate => Self::DeleteVsUpdate,
+ MergeConflictKind::OrphanEdge => Self::OrphanEdge,
+ MergeConflictKind::UniqueViolation => Self::UniqueViolation,
+ MergeConflictKind::CardinalityViolation => Self::CardinalityViolation,
+ MergeConflictKind::ValueConstraintViolation => Self::ValueConstraintViolation,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct MergeConflictOutput {
+ pub table_key: String,
+ pub row_id: Option,
+ pub kind: MergeConflictKindOutput,
+ pub message: String,
+}
+
+impl From<&MergeConflict> for MergeConflictOutput {
+ fn from(value: &MergeConflict) -> Self {
+ Self {
+ table_key: value.table_key.clone(),
+ row_id: value.row_id.clone(),
+ kind: value.kind.into(),
+ message: value.message.clone(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ReadTargetOutput {
+ pub branch: Option,
+ pub snapshot: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ReadOutput {
+ pub query_name: String,
+ pub target: ReadTargetOutput,
+ pub row_count: usize,
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
+ pub columns: Vec,
+ pub rows: Value,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ChangeOutput {
+ pub branch: String,
+ pub query_name: String,
+ pub affected_nodes: usize,
+ pub affected_edges: usize,
+ pub actor_id: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct IngestTableOutput {
+ pub table_key: String,
+ pub rows_loaded: usize,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct IngestOutput {
+ pub uri: String,
+ pub branch: String,
+ /// Base branch a fork was requested from (the request's `from`), echoed
+ /// even when the branch already existed. `null` when `from` was absent.
+ pub base_branch: Option,
+ pub branch_created: bool,
+ #[schema(value_type = LoadModeSchema)]
+ pub mode: LoadMode,
+ pub tables: Vec,
+ pub actor_id: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct CommitOutput {
+ pub graph_commit_id: String,
+ pub manifest_branch: Option,
+ pub manifest_version: u64,
+ pub parent_commit_id: Option,
+ pub merged_parent_commit_id: Option,
+ pub actor_id: Option,
+ /// Commit creation time as Unix epoch microseconds.
+ #[schema(example = 1714000000000000i64)]
+ pub created_at: i64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct CommitListOutput {
+ pub commits: Vec,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ReadRequest {
+ /// GQ query source. May declare one or more named queries; pick one with
+ /// `query_name` if there is more than one.
+ #[schema(
+ example = "query get_person($name: String) {\n match {\n $p: Person { name: $name }\n }\n return { $p.name, $p.age }\n}"
+ )]
+ pub query_source: String,
+ /// Name of the query to run when `query_source` declares multiple. Optional
+ /// when only one query is declared.
+ pub query_name: Option,
+ /// JSON object whose keys match the query's declared parameters.
+ pub params: Option,
+ /// Branch to read from. Mutually exclusive with `snapshot`. Defaults to `main`.
+ pub branch: Option,
+ /// Snapshot id to read from. Mutually exclusive with `branch`.
+ pub snapshot: Option,
+}
+
+/// Inline read-query request for `POST /query`.
+///
+/// Friendlier-named alternative to [`ReadRequest`] for ad-hoc reads and
+/// AI-agent integration. Mutations are rejected with 400 — use `POST
+/// /mutate` (or its deprecated alias `POST /change`) for write queries.
+/// Field names are deliberately short (`query`, `name`) to match the GQ
+/// keyword and the CLI `-e` flag.
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct QueryRequest {
+ /// GQ read-query source. May declare one or more named queries; pick one
+ /// with `name` when more than one is declared. Mutations
+ /// (`insert`/`update`/`delete`) get 400 — use `POST /mutate` (or its
+ /// deprecated alias `POST /change`) instead.
+ #[schema(example = "query get_person($name: String) {\n match {\n $p: Person { name: $name }\n }\n return { $p.name, $p.age }\n}")]
+ pub query: String,
+ /// Name of the query to run when `query` declares multiple. Optional when
+ /// only one query is declared.
+ pub name: Option,
+ /// JSON object whose keys match the query's declared parameters.
+ pub params: Option,
+ /// Branch to read from. Mutually exclusive with `snapshot`. Defaults to `main`.
+ pub branch: Option,
+ /// Snapshot id to read from. Mutually exclusive with `branch`.
+ pub snapshot: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ChangeRequest {
+ /// GQ mutation source containing `insert`, `update`, or `delete` statements.
+ /// May declare multiple named mutations; pick one with `name`.
+ ///
+ /// Accepts the legacy field name `query_source` as a deserialization alias.
+ #[schema(
+ example = "query insert_person($name: String, $age: I32) {\n insert Person { name: $name, age: $age }\n}"
+ )]
+ #[serde(alias = "query_source")]
+ pub query: String,
+ /// Name of the mutation to run when `query` declares multiple.
+ ///
+ /// Accepts the legacy field name `query_name` as a deserialization alias.
+ #[serde(default, alias = "query_name")]
+ pub name: Option,
+ /// JSON object whose keys match the mutation's declared parameters.
+ #[serde(default)]
+ pub params: Option,
+ /// Target branch. Defaults to `main`.
+ #[serde(default)]
+ pub branch: Option,
+}
+
+/// Body for `POST /queries/{name}` — invokes the server-side stored query
+/// named in the path. The query source and name come from the registry,
+/// never the body; only the runtime inputs are supplied here.
+#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
+pub struct InvokeStoredQueryRequest {
+ /// JSON object whose keys match the stored query's declared parameters.
+ #[serde(default)]
+ pub params: Option,
+ /// Branch to run against. Defaults to `main`; for a stored mutation the
+ /// write targets this branch.
+ #[serde(default)]
+ pub branch: Option,
+ /// Snapshot id to read from (read queries only — rejected for a stored
+ /// mutation). Mutually exclusive with `branch`.
+ #[serde(default)]
+ pub snapshot: Option,
+ /// The kind the caller expects (RFC-011 Decision 3): `Some(false)` for
+ /// `omnigraph query `, `Some(true)` for `omnigraph mutate `.
+ /// When set and it disagrees with the stored query's actual kind, the
+ /// server rejects the call (400) so the verb asserts the kind. `None`
+ /// (the default) skips the check — preserving older clients and aliases.
+ #[serde(default)]
+ pub expect_mutation: Option,
+}
+
+/// Response for `POST /queries/{name}`: the read envelope for a stored
+/// read, or the mutation envelope for a stored mutation. Serialized
+/// **untagged**, so the wire shape is exactly [`ReadOutput`] or
+/// [`ChangeOutput`] — classification follows the stored query, not a
+/// wrapper field.
+#[derive(Debug, Serialize, ToSchema)]
+#[serde(untagged)]
+pub enum InvokeStoredQueryResponse {
+ Read(ReadOutput),
+ Change(ChangeOutput),
+}
+
+/// The kind of a stored-query parameter, decomposed so a client (e.g. an
+/// MCP server) can build a typed input schema with a closed `match` and
+/// never re-parse omnigraph's type spelling. `bigint`/`date`/`datetime`/
+/// `blob` are carried as JSON strings on the wire: a 64-bit integer past
+/// 2^53 loses precision as a JSON number, and Date/DateTime are ISO
+/// strings, Blob a blob-URI string.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ParamKind {
+ String,
+ Bool,
+ Int,
+ #[serde(rename = "bigint")]
+ BigInt,
+ Float,
+ Date,
+ #[serde(rename = "datetime")]
+ DateTime,
+ Blob,
+ Vector,
+ List,
+}
+
+/// One declared parameter of a stored query, projected for the catalog.
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ParamDescriptor {
+ pub name: String,
+ pub kind: ParamKind,
+ /// Element kind when `kind == list` (always a scalar — the grammar
+ /// forbids lists of vectors or nested lists).
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub item_kind: Option,
+ /// Dimension when `kind == vector`.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub vector_dim: Option,
+ /// `false` → the caller must supply it; `true` → optional.
+ pub nullable: bool,
+}
+
+/// One entry in the stored-query catalog (`GET /queries`).
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct QueryCatalogEntry {
+ /// Registry key / invoke path segment (`POST /queries/{name}`).
+ pub name: String,
+ /// MCP tool id (the `tool_name` override, else `name`).
+ pub tool_name: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub description: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub instruction: Option,
+ /// `true` for a stored mutation → an MCP read-only hint of `false`.
+ pub mutation: bool,
+ pub params: Vec,
+}
+
+/// Response for `GET /queries`: the `mcp.expose` subset of a graph's
+/// stored-query registry, each with typed parameters.
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct QueriesCatalogOutput {
+ pub queries: Vec,
+}
+
+/// Total map from a resolved scalar to its catalog kind. Exhaustive on
+/// purpose: a new `ScalarType` is a compile error here until catalogued.
+fn scalar_kind(scalar: ScalarType) -> ParamKind {
+ match scalar {
+ ScalarType::String => ParamKind::String,
+ ScalarType::Bool => ParamKind::Bool,
+ ScalarType::I32 | ScalarType::U32 => ParamKind::Int,
+ ScalarType::I64 | ScalarType::U64 => ParamKind::BigInt,
+ ScalarType::F32 | ScalarType::F64 => ParamKind::Float,
+ ScalarType::Date => ParamKind::Date,
+ ScalarType::DateTime => ParamKind::DateTime,
+ ScalarType::Blob => ParamKind::Blob,
+ ScalarType::Vector(_) => ParamKind::Vector,
+ }
+}
+
+pub fn param_descriptor(param: &Param) -> ParamDescriptor {
+ match PropType::from_param_type_name(¶m.type_name, param.nullable) {
+ Some(pt) if pt.list => ParamDescriptor {
+ name: param.name.clone(),
+ kind: ParamKind::List,
+ item_kind: Some(scalar_kind(pt.scalar)),
+ vector_dim: None,
+ nullable: param.nullable,
+ },
+ Some(pt) => {
+ let (kind, vector_dim) = match pt.scalar {
+ ScalarType::Vector(dim) => (ParamKind::Vector, Some(dim)),
+ other => (scalar_kind(other), None),
+ };
+ ParamDescriptor {
+ name: param.name.clone(),
+ kind,
+ item_kind: None,
+ vector_dim,
+ nullable: param.nullable,
+ }
+ }
+ // Unreachable for a parsed query (every declared param type is
+ // grammatical); fall back to an opaque string so the field is still
+ // usable rather than dropped.
+ None => ParamDescriptor {
+ name: param.name.clone(),
+ kind: ParamKind::String,
+ item_kind: None,
+ vector_dim: None,
+ nullable: param.nullable,
+ },
+ }
+}
+
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
+pub struct SchemaApplyRequest {
+ /// Project schema in `.pg` source form. The diff against the current
+ /// schema produces the migration steps that will be applied.
+ #[schema(
+ example = "node Person {\n name: String @key\n age: I32?\n}\n\nedge Knows: Person -> Person"
+ )]
+ pub schema_source: String,
+ /// When true, promote every `DropMode::Soft` step in the plan to
+ /// `DropMode::Hard`, making the prior column data unreachable
+ /// after the apply. Matches the CLI's `--allow-data-loss` flag.
+ /// Defaults to `false` (drops remain reversible via time travel).
+ #[serde(default)]
+ pub allow_data_loss: bool,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct SchemaApplyOutput {
+ pub uri: String,
+ pub supported: bool,
+ pub applied: bool,
+ pub step_count: usize,
+ pub manifest_version: u64,
+ #[schema(value_type = Vec)]
+ pub steps: Vec,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct SchemaOutput {
+ pub schema_source: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct IngestRequest {
+ /// Target branch. Defaults to `main`. Without `from`, the branch must
+ /// already exist — a missing branch is a 404, never an implicit fork.
+ pub branch: Option,
+ /// Parent branch used to create `branch` if it does not exist. Branch
+ /// creation is opt-in by presence of this field; omit it to require an
+ /// existing branch.
+ pub from: Option,
+ /// How existing rows are handled. Defaults to `merge`.
+ #[schema(value_type = Option)]
+ pub mode: Option,
+ /// NDJSON payload: one record per line, each shaped
+ /// `{"type": "", "data": {...}}`.
+ #[schema(
+ example = "{\"type\": \"Person\", \"data\": {\"name\": \"Alice\", \"age\": 30}}\n{\"type\": \"Person\", \"data\": {\"name\": \"Bob\", \"age\": 25}}"
+ )]
+ pub data: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ExportRequest {
+ /// Branch to export. Defaults to `main`.
+ pub branch: Option,
+ /// Restrict the export to these node/edge type names. Empty exports all types.
+ #[serde(default)]
+ pub type_names: Vec,
+ /// Restrict the export to these table keys. Empty exports all tables.
+ #[serde(default)]
+ pub table_keys: Vec,
+}
+
+#[derive(Debug, Clone, Deserialize, IntoParams)]
+pub struct SnapshotQuery {
+ pub branch: Option,
+}
+
+#[derive(Debug, Clone, Deserialize, IntoParams)]
+pub struct CommitListQuery {
+ pub branch: Option,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct HealthOutput {
+ pub status: String,
+ pub version: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub source_version: Option,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ErrorCode {
+ Unauthorized,
+ Forbidden,
+ BadRequest,
+ NotFound,
+ /// 405 Method Not Allowed — the route exists but the active server
+ /// mode doesn't serve this method (e.g. `GET /graphs` in single-graph
+ /// mode). Distinct from 404 so clients can tell "wrong context" from
+ /// "no such resource."
+ MethodNotAllowed,
+ Conflict,
+ /// 429 Too Many Requests — per-actor admission cap exceeded.
+ /// Clients should respect the `Retry-After` header.
+ TooManyRequests,
+ Internal,
+}
+
+/// Structured details for a publisher-level OCC failure. Surfaces alongside
+/// HTTP 409 when a write was rejected because the caller's pre-write view of
+/// one table's manifest version was stale relative to the current head. The
+/// expected/actual fields tell the client which table to refresh.
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ManifestConflictOutput {
+ pub table_key: String,
+ pub expected: u64,
+ pub actual: u64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct ErrorOutput {
+ pub error: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub code: Option,
+ #[serde(default, skip_serializing_if = "Vec::is_empty")]
+ pub merge_conflicts: Vec,
+ /// Set when the conflict is a publisher CAS rejection
+ /// (`ManifestConflictDetails::ExpectedVersionMismatch`). The caller's
+ /// pre-write view of `table_key` was at version `expected` but the
+ /// manifest is now at `actual`. Refresh and retry.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub manifest_conflict: Option,
+}
+
+pub fn snapshot_payload(branch: &str, snapshot: &Snapshot) -> SnapshotOutput {
+ let mut entries: Vec<_> = snapshot.entries().cloned().collect();
+ entries.sort_by(|a, b| a.table_key.cmp(&b.table_key));
+ let tables = entries
+ .iter()
+ .map(|entry| SnapshotTableOutput {
+ table_key: entry.table_key.clone(),
+ table_path: entry.table_path.clone(),
+ table_version: entry.table_version,
+ table_branch: entry.table_branch.clone(),
+ row_count: entry.row_count,
+ })
+ .collect::>();
+ SnapshotOutput {
+ branch: branch.to_string(),
+ manifest_version: snapshot.version(),
+ tables,
+ }
+}
+
+pub fn schema_apply_output(uri: &str, result: SchemaApplyResult) -> SchemaApplyOutput {
+ SchemaApplyOutput {
+ uri: uri.to_string(),
+ supported: result.supported,
+ applied: result.applied,
+ step_count: result.steps.len(),
+ manifest_version: result.manifest_version,
+ steps: result.steps,
+ }
+}
+
+pub fn commit_output(commit: &GraphCommit) -> CommitOutput {
+ CommitOutput {
+ graph_commit_id: commit.graph_commit_id.clone(),
+ manifest_branch: commit.manifest_branch.clone(),
+ manifest_version: commit.manifest_version,
+ parent_commit_id: commit.parent_commit_id.clone(),
+ merged_parent_commit_id: commit.merged_parent_commit_id.clone(),
+ actor_id: commit.actor_id.clone(),
+ created_at: commit.created_at,
+ }
+}
+
+pub fn read_output(query_name: String, target: &ReadTarget, result: QueryResult) -> ReadOutput {
+ let columns = result
+ .schema()
+ .fields()
+ .iter()
+ .map(|field| field.name().clone())
+ .collect();
+ ReadOutput {
+ query_name,
+ target: read_target_output(target),
+ row_count: result.num_rows(),
+ columns,
+ rows: result.to_rust_json(),
+ }
+}
+
+pub fn ingest_output(
+ uri: &str,
+ result: &LoadResult,
+ mode: LoadMode,
+ actor_id: Option,
+) -> IngestOutput {
+ IngestOutput {
+ uri: uri.to_string(),
+ branch: result.branch.clone(),
+ base_branch: result.base_branch.clone(),
+ branch_created: result.branch_created,
+ mode,
+ tables: result
+ .to_ingest_tables()
+ .into_iter()
+ .map(|table| IngestTableOutput {
+ table_key: table.table_key,
+ rows_loaded: table.rows_loaded,
+ })
+ .collect(),
+ actor_id,
+ }
+}
+
+pub fn read_target_output(target: &ReadTarget) -> ReadTargetOutput {
+ match target {
+ ReadTarget::Branch(branch) => ReadTargetOutput {
+ branch: Some(branch.clone()),
+ snapshot: None,
+ },
+ ReadTarget::Snapshot(snapshot) => ReadTargetOutput {
+ branch: None,
+ snapshot: Some(snapshot.as_str().to_string()),
+ },
+ }
+}
+
+// ─── MR-668 — management endpoint shapes ──────────────────────────────────
+
+/// One entry in the response from `GET /graphs`. Cluster operators
+/// consume this list to discover which graphs the server is currently
+/// serving. The shape is intentionally minimal — `graph_id` and `uri`
+/// are the only fields a routing client needs.
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct GraphInfo {
+ pub graph_id: String,
+ pub uri: String,
+}
+
+/// Response from `GET /graphs`. Lists every graph registered with the
+/// server in alphabetical order by `graph_id` (sorted server-side so
+/// clients get deterministic output across requests).
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct GraphListResponse {
+ pub graphs: Vec,
+}
diff --git a/crates/omnigraph-cli/Cargo.toml b/crates/omnigraph-cli/Cargo.toml
index 1670fb2..e21b21e 100644
--- a/crates/omnigraph-cli/Cargo.toml
+++ b/crates/omnigraph-cli/Cargo.toml
@@ -15,6 +15,7 @@ path = "src/main.rs"
[dependencies]
omnigraph = { package = "omnigraph-engine", path = "../omnigraph", version = "0.7.0" }
omnigraph-compiler = { path = "../omnigraph-compiler", version = "0.7.0" }
+omnigraph-api-types = { path = "../omnigraph-api-types", version = "0.7.0" }
omnigraph-cluster = { path = "../omnigraph-cluster", version = "0.7.0" }
omnigraph-policy = { path = "../omnigraph-policy", version = "0.7.0" }
omnigraph-server = { path = "../omnigraph-server", version = "0.7.0" }
diff --git a/crates/omnigraph-cli/src/cli.rs b/crates/omnigraph-cli/src/cli.rs
index 7b976b4..94bec5a 100644
--- a/crates/omnigraph-cli/src/cli.rs
+++ b/crates/omnigraph-cli/src/cli.rs
@@ -9,90 +9,159 @@ pub(crate) const DEFAULT_BEARER_TOKEN_ENV: &str = "OMNIGRAPH_BEARER_TOKEN";
#[command(name = "omnigraph")]
#[command(about = "Omnigraph graph database CLI")]
#[command(version = env!("CARGO_PKG_VERSION"), disable_version_flag = true)]
+// Subcommands render in declaration order (clap can't print labeled headings
+// between groups), so this legend names the capability each command needs —
+// the user-facing vocabulary (RFC-011). `Plane` stays the internal classifier.
+#[command(after_help = "\
+COMMANDS BY CAPABILITY:\n \
+any — run against a graph, served (--server / --profile) or embedded (--store / a \
+URI): query, mutate, load, branch, snapshot, export, commit, schema show/apply.\n \
+served — require a server: graphs.\n \
+direct — direct storage access; reject --server (init, optimize, repair, cleanup, \
+schema plan, lint).\n \
+control — manage or inspect a cluster (cluster via --config; policy & queries via \
+--cluster).\n \
+local — no explicit graph scope; local config & tooling: alias, embed, login, logout, profile, version.\n\
+See the 'Command capabilities' section of the CLI reference for which flags apply where.")]
pub(crate) struct Cli {
- /// Actor identity for direct-engine writes (MR-722). Overrides
- /// `cli.actor` from `omnigraph.yaml`. When the configured policy
- /// is in effect, Cedar evaluates this actor against the requested
- /// action and scope; with policy configured but neither this flag
- /// nor `cli.actor` set, the engine-layer footgun guard fires and
- /// the write is denied (no silent bypass). Has no effect on remote
- /// HTTP writes — those resolve their actor server-side from the
- /// bearer token.
+ /// Actor id for direct-engine writes; overrides `cli.actor`. No effect on
+ /// remote writes (the server resolves the actor from the bearer token).
+ /// With a policy configured but no actor set, the write is denied — see
+ /// docs/user/operations/policy.md.
#[arg(long = "as", global = true, value_name = "ACTOR")]
pub(crate) as_actor: Option,
- /// Target an operator-defined server by name (RFC-007): resolves to
- /// its `url` from `servers:` in ~/.omnigraph/config.yaml. Exclusive
- /// with a positional URI or `--target`.
- #[arg(long, global = true, value_name = "NAME")]
+ /// Address a server by name (resolves to its `url` from `servers:` in
+ /// ~/.omnigraph/config.yaml) or by a literal `http(s)://` URL. Exclusive
+ /// with a positional URI.
+ #[arg(long, global = true, value_name = "NAME|URL")]
pub(crate) server: Option,
- /// Graph id on a multi-graph `--server` (appends `/graphs/` to
- /// the server url). Requires --server.
- #[arg(long, global = true, value_name = "GRAPH_ID", requires = "server")]
+ /// Select a graph within a multi-graph scope: on a `--server` it appends
+ /// `/graphs/` to the server url; on a `--cluster` it picks which
+ /// cluster graph to maintain. Rejected on a single-graph address (a
+ /// positional URI / `--store`).
+ #[arg(long, global = true, value_name = "GRAPH_ID")]
pub(crate) graph: Option,
+ /// Select a named scope bundle (RFC-011) from `profiles:` in
+ /// ~/.omnigraph/config.yaml: fills in this command's omitted addressing
+ /// (server/cluster/store + default graph). Falls back to
+ /// $OMNIGRAPH_PROFILE. Config data, not state — every command resolves
+ /// scope fresh.
+ #[arg(long, global = true, value_name = "NAME")]
+ pub(crate) profile: Option,
+
+ /// Address a single graph's storage directly (RFC-011): a `file://` /
+ /// `s3://` store URI. Explicit, ad-hoc direct access — bypasses any
+ /// server. Exclusive with a positional URI / `--server`.
+ #[arg(long, global = true, value_name = "URI")]
+ pub(crate) store: Option,
+
+ /// Address a cluster-managed graph's storage for maintenance (RFC-011):
+ /// a cluster directory or storage-root URI — named via `clusters:` in
+ /// ~/.omnigraph/config.yaml, or a literal `file://`/`s3://` root. Pair
+ /// with `--graph ` to select the graph. Used by optimize / repair /
+ /// cleanup; exclusive with a positional URI / `--store` / `--server`.
+ #[arg(long, global = true, value_name = "DIR|URI")]
+ pub(crate) cluster: Option,
+
+ /// Skip the confirmation prompt for a destructive write (`cleanup`,
+ /// overwrite `load`, `branch delete`) against a non-local scope (RFC-011
+ /// Decision 9). Without it, a non-local destructive write prompts on a TTY
+ /// and refuses (errors) when there is no TTY or `--json` is set.
+ #[arg(long, global = true)]
+ pub(crate) yes: bool,
+
+ /// Suppress the one-line resolved-write-target diagnostic that write
+ /// commands echo to stderr (RFC-011 Decision 9).
+ #[arg(long, global = true)]
+ pub(crate) quiet: bool,
+
#[command(subcommand)]
pub(crate) command: Command,
}
#[derive(Debug, Subcommand)]
pub(crate) enum Command {
- /// Print the CLI version
- Version,
- /// Store a bearer token for a named server in ~/.omnigraph/credentials
- /// (0600). Token from --token or one line on stdin:
- /// `echo $TOKEN | omnigraph login prod`. The keyed token applies to
- /// requests whose URL matches the server's `url` in the operator
- /// config's `servers:` map.
- Login {
- /// Server name (keys the credential; declare its url under
- /// `servers:` in ~/.omnigraph/config.yaml)
- name: String,
- /// The token. Prefer piping via stdin over this flag (shell
- /// history).
+ // ── Data plane ── run against a graph (embedded or via --server).
+ /// Execute a read query against a branch or snapshot.
+ ///
+ /// Canonical read endpoint. The previous name `omnigraph read` is
+ /// kept as a visible alias and prints a one-line deprecation warning
+ /// when used. Pairs with `omnigraph mutate` on the write side.
+ #[command(visible_alias = "read")]
+ Query {
+ /// Query name. With no `--query`/`-e`, the stored query to invoke from
+ /// the catalog (served — addressed via --server/--profile). With
+ /// `--query`/`-e`, selects which query in that ad-hoc source to run.
+ name: Option,
+ /// Ad-hoc query file (a `.gq` you're authoring / break-glass).
+ #[arg(long, conflicts_with = "query_string")]
+ query: Option,
+ /// Inline ad-hoc GQ source — alternative to `--query `.
+ #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with = "query")]
+ query_string: Option,
+ #[command(flatten)]
+ params: ParamsArgs,
+ #[arg(long, conflicts_with = "snapshot")]
+ branch: Option,
+ #[arg(long, conflicts_with = "branch")]
+ snapshot: Option,
+ #[arg(long, conflicts_with = "json")]
+ format: Option,
+ #[arg(long, conflicts_with = "format")]
+ json: bool,
+ },
+ /// Execute a graph mutation query against a branch.
+ ///
+ /// Canonical mutation endpoint. The previous name `omnigraph change`
+ /// is kept as a visible alias and prints a one-line deprecation
+ /// warning when used. Pairs with `omnigraph query` on the read side.
+ #[command(visible_alias = "change")]
+ Mutate {
+ /// Query name. With no `--query`/`-e`, the stored mutation to invoke
+ /// from the catalog (served — addressed via --server/--profile). With
+ /// `--query`/`-e`, selects which query in that ad-hoc source to run.
+ name: Option,
+ /// Ad-hoc mutation file (a `.gq` you're authoring / break-glass).
+ #[arg(long, conflicts_with = "query_string")]
+ query: Option,
+ /// Inline ad-hoc GQ source — alternative to `--query `.
+ #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with = "query")]
+ query_string: Option,
+ #[command(flatten)]
+ params: ParamsArgs,
#[arg(long)]
- token: Option,
+ branch: Option,
#[arg(long)]
json: bool,
},
- /// Legacy-config tooling (RFC-008): split omnigraph.yaml into its
- /// two destinations.
- Config {
- #[command(subcommand)]
- command: ConfigCommand,
- },
- /// Remove a named server's stored credential. Idempotent.
- Logout {
+ /// Invoke an operator alias (RFC-011 Decision 4).
+ ///
+ /// An alias is a personal binding under `aliases:` in
+ /// ~/.omnigraph/config.yaml — name → (server, graph, stored-query name,
+ /// default params). `omnigraph alias [args]` invokes the bound
+ /// stored query on its server. Living in its own namespace, an alias can
+ /// never shadow or be shadowed by a built-in verb. Replaces the removed
+ /// `--alias` flag on `query`/`mutate`.
+ Alias {
+ /// Alias name (a key under `aliases:` in ~/.omnigraph/config.yaml).
name: String,
- #[arg(long)]
+ /// Positional args bound to the alias's declared `args` params, in order.
+ args: Vec,
+ #[command(flatten)]
+ params: ParamsArgs,
+ #[arg(long, conflicts_with = "json")]
+ format: Option,
+ #[arg(long, conflicts_with = "format")]
json: bool,
},
- /// Generate, clean, or refresh explicit seed embeddings
- Embed(EmbedArgs),
- /// Initialize a new graph from a schema
- Init {
- #[arg(long)]
- schema: PathBuf,
- /// Graph URI (local path or s3://)
- uri: String,
- /// Overwrite existing schema artifacts at the URI. Without
- /// this flag, init refuses to touch a URI that already holds
- /// `_schema.pg`, `_schema.ir.json`, or `__schema_state.json`
- /// — closes the re-init footgun (MR-668 follow-up). With the
- /// flag, the operator opts in to destructive semantics.
- #[arg(long)]
- force: bool,
- },
/// Load data into a graph (local or remote)
Load {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
data: PathBuf,
/// Target branch (defaults to main). Without --from it must exist.
#[arg(long)]
@@ -109,14 +178,11 @@ pub(crate) enum Command {
json: bool,
},
/// Deprecated alias of `load --from ` (defaults: --mode merge, --from main)
+ #[command(hide = true)]
Ingest {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
data: PathBuf,
#[arg(long)]
branch: Option,
@@ -132,11 +198,99 @@ pub(crate) enum Command {
#[command(subcommand)]
command: BranchCommand,
},
+ /// Show graph snapshot
+ Snapshot {
+ /// Graph URI
+ uri: Option,
+ #[arg(long)]
+ branch: Option,
+ #[arg(long)]
+ json: bool,
+ },
+ /// Export a full graph snapshot as JSONL
+ Export {
+ /// Graph URI
+ uri: Option,
+ #[arg(long)]
+ branch: Option,
+ #[arg(long, hide = true)]
+ jsonl: bool,
+ #[arg(long = "type")]
+ type_names: Vec,
+ #[arg(long = "table")]
+ table_keys: Vec,
+ },
+ /// Commit history operations
+ Commit {
+ #[command(subcommand)]
+ command: CommitCommand,
+ },
/// Schema planning operations
Schema {
#[command(subcommand)]
command: SchemaCommand,
},
+ /// Manage graphs on a multi-graph server (MR-668)
+ Graphs {
+ #[command(subcommand)]
+ command: GraphsCommand,
+ },
+
+ // ── Storage / local graph ops ── direct storage or local files; reject --server.
+ /// Initialize a new graph from a schema
+ Init {
+ #[arg(long)]
+ schema: PathBuf,
+ /// Graph URI (local path or s3://)
+ uri: String,
+ /// Overwrite existing schema artifacts at the URI. Without
+ /// this flag, init refuses to touch a URI that already holds
+ /// `_schema.pg`, `_schema.ir.json`, or `__schema_state.json`
+ /// — closes the re-init footgun (MR-668 follow-up). With the
+ /// flag, the operator opts in to destructive semantics.
+ #[arg(long)]
+ force: bool,
+ },
+ /// Compact small Lance fragments in every table of the graph
+ Optimize {
+ /// Graph URI
+ uri: Option,
+ #[arg(long)]
+ json: bool,
+ },
+ /// Classify and explicitly repair manifest/head drift
+ Repair {
+ /// Graph URI
+ uri: Option,
+ /// Publish verified maintenance drift. Without this flag, repair only
+ /// previews what it would do.
+ #[arg(long)]
+ confirm: bool,
+ /// Also publish suspicious or unverifiable drift. Requires
+ /// `--confirm`; use only after operator review.
+ #[arg(long, requires = "confirm")]
+ force: bool,
+ #[arg(long)]
+ json: bool,
+ },
+ /// Remove old Lance versions from every table of the graph (destructive)
+ Cleanup {
+ /// Graph URI
+ uri: Option,
+ /// Number of recent versions to keep per table. Either `--keep` or
+ /// `--older-than` (or both) must be set.
+ #[arg(long)]
+ keep: Option,
+ /// Only remove versions older than this duration. Accepts Go-style
+ /// durations: `7d`, `24h`, `90m`. At least one of --keep / --older-than.
+ #[arg(long)]
+ older_than: Option,
+ /// Required to actually run; without it, prints what would be removed
+ #[arg(long)]
+ confirm: bool,
+ #[arg(long)]
+ json: bool,
+ },
/// Validate queries against a schema (offline) or repo (repo-backed).
///
/// Canonical name is `lint` (matches the `omnigraph_compiler::lint`
@@ -152,10 +306,6 @@ pub(crate) enum Command {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
query: PathBuf,
#[arg(long)]
schema: Option,
@@ -167,179 +317,63 @@ pub(crate) enum Command {
#[command(subcommand)]
command: QueriesCommand,
},
- /// Show graph snapshot
- Snapshot {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
- branch: Option,
- #[arg(long)]
- json: bool,
- },
- /// Export a full graph snapshot as JSONL
- Export {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
- branch: Option,
- #[arg(long, hide = true)]
- jsonl: bool,
- #[arg(long = "type")]
- type_names: Vec,
- #[arg(long = "table")]
- table_keys: Vec,
- },
- /// Commit history operations
- Commit {
- #[command(subcommand)]
- command: CommitCommand,
- },
- /// Execute a read query against a branch or snapshot.
- ///
- /// Canonical read endpoint. The previous name `omnigraph read` is
- /// kept as a visible alias and prints a one-line deprecation warning
- /// when used. Pairs with `omnigraph mutate` on the write side.
- #[command(visible_alias = "read")]
- Query {
- /// Graph URI
- #[arg(long)]
- uri: Option,
- #[arg(hide = true)]
- legacy_uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long, conflicts_with_all = ["query", "query_string"])]
- alias: Option,
- #[arg(long, conflicts_with_all = ["alias", "query_string"])]
- query: Option,
- /// Inline GQ source — alternative to `--query ` and `--alias `.
- #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with_all = ["query", "alias"])]
- query_string: Option,
- #[arg(long)]
- name: Option,
- #[command(flatten)]
- params: ParamsArgs,
- #[arg(long, conflicts_with = "snapshot")]
- branch: Option,
- #[arg(long, conflicts_with = "branch")]
- snapshot: Option,
- #[arg(long, conflicts_with = "json")]
- format: Option,
- #[arg(long, conflicts_with = "format")]
- json: bool,
- #[arg()]
- alias_args: Vec,
- },
- /// Execute a graph mutation query against a branch.
- ///
- /// Canonical mutation endpoint. The previous name `omnigraph change`
- /// is kept as a visible alias and prints a one-line deprecation
- /// warning when used. Pairs with `omnigraph query` on the read side.
- #[command(visible_alias = "change")]
- Mutate {
- /// Graph URI
- #[arg(long)]
- uri: Option,
- #[arg(hide = true)]
- legacy_uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long, conflicts_with_all = ["query", "query_string"])]
- alias: Option,
- #[arg(long, conflicts_with_all = ["alias", "query_string"])]
- query: Option,
- /// Inline GQ source — alternative to `--query ` and `--alias `.
- #[arg(short = 'e', long = "query-string", value_name = "GQ", conflicts_with_all = ["query", "alias"])]
- query_string: Option,
- #[arg(long)]
- name: Option,
- #[command(flatten)]
- params: ParamsArgs,
- #[arg(long)]
- branch: Option,
- #[arg(long)]
- json: bool,
- #[arg()]
- alias_args: Vec,
- },
- /// Policy administration and diagnostics
- Policy {
- #[command(subcommand)]
- command: PolicyCommand,
- },
- /// Compact small Lance fragments in every table of the graph
- Optimize {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
- json: bool,
- },
- /// Classify and explicitly repair manifest/head drift
- Repair {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- /// Publish verified maintenance drift. Without this flag, repair only
- /// previews what it would do.
- #[arg(long)]
- confirm: bool,
- /// Also publish suspicious or unverifiable drift. Requires
- /// `--confirm`; use only after operator review.
- #[arg(long, requires = "confirm")]
- force: bool,
- #[arg(long)]
- json: bool,
- },
- /// Remove old Lance versions from every table of the graph (destructive)
- Cleanup {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- /// Number of recent versions to keep per table. Either `--keep` or
- /// `--older-than` (or both) must be set.
- #[arg(long)]
- keep: Option,
- /// Only remove versions older than this duration. Accepts Go-style
- /// durations: `7d`, `24h`, `90m`. At least one of --keep / --older-than.
- #[arg(long)]
- older_than: Option,
- /// Required to actually run; without it, prints what would be removed
- #[arg(long)]
- confirm: bool,
- #[arg(long)]
- json: bool,
- },
+
+ // ── Control plane ── manage a cluster directory (--config ).
/// Validate and plan read-only cluster configuration.
Cluster {
#[command(subcommand)]
command: ClusterCommand,
},
- /// Manage graphs on a multi-graph server (MR-668)
- Graphs {
+
+ /// Policy administration and diagnostics against a cluster's applied bundles
+ Policy {
#[command(subcommand)]
- command: GraphsCommand,
+ command: PolicyCommand,
+ },
+ /// Generate, clean, or refresh explicit seed embeddings
+ Embed(EmbedArgs),
+ /// Store a bearer token for a named server (0600 credentials file). Token
+ /// via --token or piped on stdin; see the CLI reference for token resolution.
+ Login {
+ /// Server name (keys the credential; declare its url under
+ /// `servers:` in ~/.omnigraph/config.yaml)
+ name: String,
+ /// The token. Prefer piping via stdin over this flag (shell
+ /// history).
+ #[arg(long)]
+ token: Option,
+ #[arg(long)]
+ json: bool,
+ },
+ /// Remove a named server's stored credential. Idempotent.
+ Logout {
+ name: String,
+ #[arg(long)]
+ json: bool,
+ },
+ /// Inspect the scope profiles in ~/.omnigraph/config.yaml (read-only).
+ Profile {
+ #[command(subcommand)]
+ command: ProfileCommand,
+ },
+ /// Print the CLI version
+ Version,
+}
+
+#[derive(Debug, Subcommand)]
+pub(crate) enum ProfileCommand {
+ /// List the profiles defined in ~/.omnigraph/config.yaml.
+ List {
+ #[arg(long)]
+ json: bool,
+ },
+ /// Show a profile's resolved scope. With no name, shows the active
+ /// (`$OMNIGRAPH_PROFILE`) profile, else the flat operator defaults.
+ Show {
+ /// Profile name (optional).
+ name: Option,
+ #[arg(long)]
+ json: bool,
},
}
@@ -439,10 +473,6 @@ pub(crate) enum GraphsCommand {
#[arg(long)]
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
json: bool,
},
}
@@ -455,10 +485,6 @@ pub(crate) enum BranchCommand {
#[arg(long)]
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
from: Option,
name: String,
#[arg(long)]
@@ -470,10 +496,6 @@ pub(crate) enum BranchCommand {
#[arg(long)]
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
json: bool,
},
/// Delete a branch
@@ -481,10 +503,6 @@ pub(crate) enum BranchCommand {
/// Graph URI
#[arg(long)]
uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
name: String,
#[arg(long)]
json: bool,
@@ -494,10 +512,6 @@ pub(crate) enum BranchCommand {
/// Graph URI
#[arg(long)]
uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
source: String,
#[arg(long)]
into: Option,
@@ -513,10 +527,6 @@ pub(crate) enum SchemaCommand {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
schema: PathBuf,
#[arg(long)]
json: bool,
@@ -531,10 +541,6 @@ pub(crate) enum SchemaCommand {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
schema: PathBuf,
#[arg(long)]
json: bool,
@@ -556,10 +562,6 @@ pub(crate) enum SchemaCommand {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
json: bool,
},
}
@@ -572,10 +574,6 @@ pub(crate) enum CommitCommand {
/// Graph URI
uri: Option,
#[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
- #[arg(long)]
branch: Option,
#[arg(long)]
json: bool,
@@ -585,10 +583,6 @@ pub(crate) enum CommitCommand {
/// Graph URI
#[arg(long)]
uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
commit_id: String,
#[arg(long)]
json: bool,
@@ -597,20 +591,24 @@ pub(crate) enum CommitCommand {
#[derive(Debug, Subcommand)]
pub(crate) enum PolicyCommand {
- /// Validate policy YAML and compiled Cedar policy state
- Validate {
- #[arg(long)]
- config: Option,
- },
- /// Run declarative policy tests from policy.tests.yaml
+ /// Compile and validate the Cedar policy bundle(s) applied in a cluster.
+ ///
+ /// Sources the bundle(s) from the cluster's applied policies
+ /// (`--cluster `); pass the global `--graph ` to pick one
+ /// graph's bundle when several apply.
+ Validate {},
+ /// Run declarative policy tests against a cluster's applied bundle.
+ ///
+ /// The cluster model has no per-bundle tests file, so the cases are
+ /// supplied explicitly with `--tests ` and checked against the
+ /// bundle selected by `--cluster` (+ optional `--graph`).
Test {
+ /// Path to a policy.tests.yaml file.
#[arg(long)]
- config: Option,
+ tests: PathBuf,
},
- /// Explain one policy decision locally
+ /// Explain one policy decision against a cluster's applied bundle.
Explain {
- #[arg(long)]
- config: Option,
#[arg(long)]
actor: String,
#[arg(long)]
@@ -624,28 +622,19 @@ pub(crate) enum PolicyCommand {
#[derive(Debug, Subcommand)]
pub(crate) enum QueriesCommand {
- /// Type-check the stored-query registry against the live schema.
+ /// Type-check a cluster's stored-query registry against its schemas.
///
- /// Distinct from `omnigraph lint` (which lints one `.gq` file):
- /// this validates the whole `queries:` registry — opening the graph
- /// to read its schema and confirming every stored query still
- /// type-checks. Exits non-zero on any breakage.
+ /// Distinct from `omnigraph lint` (which lints one `.gq` file): this
+ /// validates the whole `queries:` registry of a cluster (`--cluster
+ /// `, optional `--graph `) by reading each graph's applied
+ /// schema and confirming every stored query still type-checks. Exits
+ /// non-zero on any breakage.
Validate {
- /// Graph URI
- uri: Option,
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
#[arg(long)]
json: bool,
},
- /// List the registered stored queries (name, MCP exposure, params).
+ /// List a cluster's registered stored queries (name, params).
List {
- #[arg(long)]
- target: Option,
- #[arg(long)]
- config: Option,
#[arg(long)]
json: bool,
},
@@ -676,7 +665,6 @@ impl From for LoadMode {
}
}
}
-
impl CliLoadMode {
pub(crate) fn as_str(self) -> &'static str {
match self {
@@ -686,21 +674,3 @@ impl CliLoadMode {
}
}
}
-
-#[derive(Debug, Subcommand)]
-pub(crate) enum ConfigCommand {
- /// Propose (and with --write, apply) the RFC-008 split of a legacy
- /// omnigraph.yaml: team half -> a ready-to-review cluster.yaml,
- /// personal half -> ~/.omnigraph/config.yaml (key-level merge,
- /// existing entries always win). Touches nothing without --write.
- Migrate {
- /// Path to the legacy omnigraph.yaml (default: ./omnigraph.yaml)
- #[arg(long)]
- config: Option,
- /// Apply the split instead of only printing it
- #[arg(long)]
- write: bool,
- #[arg(long)]
- json: bool,
- },
-}
diff --git a/crates/omnigraph-cli/src/client.rs b/crates/omnigraph-cli/src/client.rs
new file mode 100644
index 0000000..7151f5e
--- /dev/null
+++ b/crates/omnigraph-cli/src/client.rs
@@ -0,0 +1,821 @@
+//! `GraphClient` — the one place the embedded-vs-remote split lives
+//! (RFC-009 Phase 3). A CLI command body calls a verb method; the
+//! enum routes to the engine (local URI) or HTTP (remote URI). The
+//! 15 per-command `if graph.is_remote { … } else { … }` forks collapse
+//! into two arms here.
+//!
+//! Phase 3a put the factory + the uniform read verbs in place. Phase 3b
+//! adds the data-plane writes (`load`/`ingest`/`mutate`/`branch_*`/
+//! `apply_schema`) and `query`. The wrinkle 3a deferred: writes open the
+//! local engine WITH policy (`open_local_db_with_policy`) and carry a
+//! resolved actor, while reads/`query` open WITHOUT policy. So the
+//! `Embedded` variant grows an optional policy context (`graph`/`actor`)
+//! and a second factory (`resolve_with_policy`) fills it; `resolve()`
+//! leaves it empty. The open path picks itself from whether `graph` is
+//! set, preserving today's two behaviors exactly. Export + graphs-list
+//! land in 3c. Behavior is unchanged per verb — the Phase-1 parity matrix
+//! is the referee and stays textually unchanged.
+//!
+//! Enum, not a trait (RFC sketch said "trait"): only two variants ever,
+//! and inherent async methods sidestep `async_trait` boxing plus the
+//! `apply_schema` catalog-validator closure that is not object-safe.
+//! Same one-body-two-impls collapse, less ceremony.
+
+use std::io::Write;
+
+use color_eyre::Result;
+use color_eyre::eyre::bail;
+use omnigraph::db::{Omnigraph, ReadTarget};
+use omnigraph_api_types::{
+ BranchCreateOutput, BranchCreateRequest, BranchDeleteOutput, BranchListOutput,
+ BranchMergeOutput, BranchMergeRequest, ChangeOutput, CommitListOutput, CommitOutput,
+ ErrorOutput, ExportRequest, GraphListResponse, IngestOutput, IngestRequest,
+ InvokeStoredQueryRequest, ReadOutput,
+ ReadRequest, SchemaApplyOutput, SchemaApplyRequest, SchemaOutput, SnapshotOutput, commit_output,
+ ingest_output, read_output, schema_apply_output, snapshot_payload,
+};
+use omnigraph_compiler::catalog::Catalog;
+use reqwest::Method;
+use serde_json::Value;
+
+use crate::cli::CliLoadMode;
+use crate::helpers::{
+ apply_bearer_token, apply_server_flag, build_http_client, is_remote_uri,
+ legacy_change_request_body, query_params_from_json,
+ remote_json, remote_url, resolve_cli_actor, resolve_cli_graph, resolve_remote_bearer_token,
+ resolve_server_flag, select_named_query,
+};
+use crate::output::{LoadOutput, load_output_from_result, load_output_from_tables};
+
+pub(crate) enum GraphClient {
+ /// Local engine at `uri`. Reads (`resolve()`) leave `actor` empty;
+ /// writes (`resolve_with_policy()`) attribute the resolved actor.
+ /// Direct-store access carries no Cedar policy (RFC-011: policy lives
+ /// in the cluster/server, not in per-operator addressing).
+ Embedded {
+ uri: String,
+ actor: Option,
+ },
+ /// Remote HTTP server. The actor is resolved server-side from the
+ /// token; the client never sets identity.
+ Remote {
+ http: reqwest::Client,
+ base_url: String,
+ token: Option,
+ },
+}
+
+/// RFC-011 Decision 7: a server scope that selects no graph (no `--graph`, no
+/// `default_graph`) must not silently fall through to the bare server URL when
+/// the server is multi-graph. Best-effort probe `GET /graphs`: a populated list
+/// forces `--graph` (listing the candidates); a single-graph/flat server (405),
+/// a policy-gated `/graphs`, or an unreachable server all proceed — the bare URL
+/// is then correct, or the real request surfaces the failure. Only fires on the
+/// no-graph path, so a `--graph`/`default_graph` happy path does no extra I/O.
+async fn require_graph_for_multi_graph_server(
+ scope: &crate::scope::ResolvedScope,
+) -> Result<()> {
+ let (Some(server), None) = (scope.server.as_deref(), scope.graph.as_deref()) else {
+ return Ok(());
+ };
+ let Some(base) = resolve_server_flag(Some(server), None)? else {
+ return Ok(());
+ };
+ let token = resolve_remote_bearer_token(Some(&base))?;
+ let probe = GraphClient::Remote {
+ http: build_http_client()?,
+ base_url: base,
+ token,
+ };
+ if let Ok(resp) = probe.list_graphs().await {
+ if !resp.graphs.is_empty() {
+ let ids: Vec<&str> = resp.graphs.iter().map(|g| g.graph_id.as_str()).collect();
+ bail!(
+ "server scope '{server}' has {} {}: [{}]; pass --graph to select one \
+ (or set `default_graph` in your operator config)",
+ ids.len(),
+ if ids.len() == 1 { "graph" } else { "graphs" },
+ ids.join(", ")
+ );
+ }
+ }
+ Ok(())
+}
+
+/// A remote graph must be addressed with `--server` (RFC-011): a positional or
+/// `--uri` `http(s)://` URL no longer auto-dispatches to a server. A remote URL
+/// produced by a server scope (`via_server`) is fine.
+fn reject_positional_remote(via_server: bool, uri: &str) -> Result<()> {
+ if !via_server && is_remote_uri(uri) {
+ bail!(
+ "a remote graph must be addressed with `--server ` — a positional \
+ (or `--uri`) http(s):// URL no longer dispatches to a server"
+ );
+ }
+ Ok(())
+}
+
+impl GraphClient {
+ /// Resolve the addressing (positional URI / `--target` / `--server`)
+ /// and credential once, then pick the variant by URI scheme — the
+ /// single branch point that replaces every per-command `is_remote`
+ /// fork. Mirrors the read verbs' current preamble (`resolve_uri`
+ /// path, not the policy-bearing `resolve_cli_graph`). Used by reads
+ /// and `query` (which opens without policy, like the reads).
+ pub(crate) async fn resolve(
+ server: Option<&str>,
+ graph: Option<&str>,
+ uri: Option,
+ profile: Option<&str>,
+ store: Option<&str>,
+ ) -> Result {
+ // RFC-011: a scope (profile / --store / operator defaults) may stand in
+ // for omitted addressing. The explicit branch passes server/graph/uri
+ // straight through, so existing invocations are unchanged.
+ let scope = crate::scope::resolve_scope(
+ &crate::operator::load_operator_config()?,
+ crate::planes::Capability::Any,
+ crate::scope::ScopeFlags { profile, store, server, cluster: None, graph, uri },
+ )?;
+ require_graph_for_multi_graph_server(&scope).await?;
+ let (server, graph, uri) = (
+ scope.server.as_deref(),
+ scope.graph.as_deref(),
+ scope.uri,
+ );
+ let via_server = server.is_some();
+ let uri = apply_server_flag(server, graph, uri)?;
+ let token = resolve_remote_bearer_token(uri.as_deref())?;
+ let uri = crate::helpers::resolve_uri(uri)?;
+ reject_positional_remote(via_server, &uri)?;
+ if is_remote_uri(&uri) {
+ Ok(GraphClient::Remote {
+ http: build_http_client()?,
+ base_url: uri,
+ token,
+ })
+ } else {
+ Ok(GraphClient::Embedded { uri, actor: None })
+ }
+ }
+
+ /// Write-path factory: the same addressing/credential resolution as
+ /// `resolve()`, but through the stricter `resolve_cli_graph` (which
+ /// carries `policy_file`/`graph_id`/`selected`), and with the actor
+ /// resolved up front. The embedded arm then opens WITH policy. The
+ /// resolution order matches the write arms exactly: server flag →
+ /// bearer token → graph.
+ pub(crate) async fn resolve_with_policy(
+ server: Option<&str>,
+ graph: Option<&str>,
+ uri: Option,
+ cli_as: Option<&str>,
+ profile: Option<&str>,
+ store: Option<&str>,
+ ) -> Result {
+ // RFC-011 scope translation (see `resolve`); explicit addressing passes
+ // through unchanged.
+ let scope = crate::scope::resolve_scope(
+ &crate::operator::load_operator_config()?,
+ crate::planes::Capability::Any,
+ crate::scope::ScopeFlags { profile, store, server, cluster: None, graph, uri },
+ )?;
+ require_graph_for_multi_graph_server(&scope).await?;
+ let (server, graph, uri) = (
+ scope.server.as_deref(),
+ scope.graph.as_deref(),
+ scope.uri,
+ );
+ let via_server = server.is_some();
+ let uri = apply_server_flag(server, graph, uri)?;
+ let token = resolve_remote_bearer_token(uri.as_deref())?;
+ let resolved = resolve_cli_graph(uri)?;
+ reject_positional_remote(via_server, &resolved.uri)?;
+ if resolved.is_remote {
+ // A served write resolves the actor server-side from the bearer
+ // token; `--as` cannot set identity here and is rejected.
+ if cli_as.is_some() {
+ bail!(
+ "`--as` is not allowed on a served write — the server resolves the actor \
+ from the bearer token. Remove `--as`, or run the write directly against \
+ storage with `--store `."
+ );
+ }
+ Ok(GraphClient::Remote {
+ http: build_http_client()?,
+ base_url: resolved.uri,
+ token,
+ })
+ } else {
+ let actor = resolve_cli_actor(cli_as)?;
+ Ok(GraphClient::Embedded {
+ uri: resolved.uri,
+ actor,
+ })
+ }
+ }
+
+ /// The graph URI (local path / remote base URL) this client addresses.
+ pub(crate) fn uri(&self) -> &str {
+ match self {
+ GraphClient::Embedded { uri, .. } => uri,
+ GraphClient::Remote { base_url, .. } => base_url,
+ }
+ }
+
+ pub(crate) fn is_remote(&self) -> bool {
+ matches!(self, GraphClient::Remote { .. })
+ }
+
+ /// Open the local engine. Direct-store access carries no Cedar policy
+ /// (RFC-011), so both read and write paths open bare; the actor is still
+ /// attributed on the write via the `_as` engine APIs.
+ async fn open_embedded(uri: &str) -> Result {
+ Ok(Omnigraph::open(uri).await?)
+ }
+
+ pub(crate) async fn branch_list(&self) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::GET,
+ remote_url(base_url, &["branches"], &[])?,
+ None,
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let db = Omnigraph::open(uri).await?;
+ let mut branches = db.branch_list().await?;
+ branches.sort();
+ Ok(BranchListOutput { branches })
+ }
+ }
+ }
+
+ pub(crate) async fn snapshot(&self, branch: &str) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::GET,
+ remote_url(base_url, &["snapshot"], &[("branch", branch)])?,
+ None,
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let db = Omnigraph::open(uri).await?;
+ let snapshot = db.snapshot_of(ReadTarget::branch(branch)).await?;
+ Ok(snapshot_payload(branch, &snapshot))
+ }
+ }
+ }
+
+ pub(crate) async fn schema_source(&self) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::GET,
+ remote_url(base_url, &["schema"], &[])?,
+ None,
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let db = Omnigraph::open(uri).await?;
+ Ok(SchemaOutput {
+ schema_source: db.schema_source().to_string(),
+ })
+ }
+ }
+ }
+
+ pub(crate) async fn list_commits(&self, branch: Option<&str>) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ let url = match branch {
+ Some(branch) => remote_url(base_url, &["commits"], &[("branch", branch)])?,
+ None => remote_url(base_url, &["commits"], &[])?,
+ };
+ remote_json(http, Method::GET, url, None, token.as_deref()).await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let db = Omnigraph::open(uri).await?;
+ let commits = db
+ .list_commits(branch)
+ .await?
+ .iter()
+ .map(commit_output)
+ .collect::>();
+ Ok(CommitListOutput { commits })
+ }
+ }
+ }
+
+ pub(crate) async fn get_commit(&self, commit_id: &str) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::GET,
+ remote_url(base_url, &["commits", commit_id], &[])?,
+ None,
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let db = Omnigraph::open(uri).await?;
+ Ok(commit_output(&db.get_commit(commit_id).await?))
+ }
+ }
+ }
+
+ /// `load` — bulk-load `data` (a file path) onto `branch`, forking from
+ /// `from` if missing. Returns the CLI `LoadOutput`; each arm keeps its
+ /// own mapping (remote sums the wire `IngestOutput.tables`, embedded
+ /// reads the richer `LoadResult` directly) — preserved exactly.
+ pub(crate) async fn load(
+ &self,
+ branch: &str,
+ from: Option<&str>,
+ data: &str,
+ mode: CliLoadMode,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ let data = std::fs::read_to_string(data)?;
+ // RFC-009 Phase 5: the canonical `load` verb targets the
+ // canonical `/load` route (the deprecated `ingest` verb below
+ // still rides `/ingest`).
+ let output = remote_json::(
+ http,
+ Method::POST,
+ remote_url(base_url, &["load"], &[])?,
+ Some(serde_json::to_value(IngestRequest {
+ branch: Some(branch.to_string()),
+ from: from.map(ToOwned::to_owned),
+ mode: Some(mode.into()),
+ data,
+ })?),
+ token.as_deref(),
+ )
+ .await?;
+ Ok(load_output_from_tables(base_url, branch, mode.as_str(), &output))
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let result = db
+ .load_file_as(branch, from, data, mode.into(), actor.as_deref())
+ .await?;
+ Ok(load_output_from_result(uri, branch, mode.as_str(), &result))
+ }
+ }
+ }
+
+ /// `ingest` — the deprecated alias of `load`. Same operation, but the
+ /// surfaced shape is the wire `IngestOutput` (printed by
+ /// `print_ingest_human`), so it is its own method. The embedded arm
+ /// echoes `actor_id: None` in the output exactly as the legacy arm did
+ /// (the actor is still attributed on the commit via `load_file_as`).
+ pub(crate) async fn ingest(
+ &self,
+ branch: &str,
+ from: &str,
+ data: &str,
+ mode: CliLoadMode,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ let data = std::fs::read_to_string(data)?;
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["ingest"], &[])?,
+ Some(serde_json::to_value(IngestRequest {
+ branch: Some(branch.to_string()),
+ from: Some(from.to_string()),
+ mode: Some(mode.into()),
+ data,
+ })?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let result = db
+ .load_file_as(branch, Some(from), data, mode.into(), actor.as_deref())
+ .await?;
+ Ok(ingest_output(uri, &result, mode.into(), None))
+ }
+ }
+ }
+
+ /// `mutate` — run a change query against `branch`. Folds
+ /// `execute_change` / `execute_change_remote` + the legacy request body.
+ pub(crate) async fn mutate(
+ &self,
+ branch: &str,
+ query_source: &str,
+ query_name: Option<&str>,
+ params_json: Option<&Value>,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["change"], &[])?,
+ Some(legacy_change_request_body(
+ query_source,
+ query_name,
+ branch,
+ params_json,
+ )),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let (selected_name, query_params) = select_named_query(query_source, query_name)?;
+ let params = query_params_from_json(&query_params, params_json)?;
+ let db = Self::open_embedded(uri).await?;
+ let actor = actor.as_deref();
+ let result = db
+ .mutate_as(branch, query_source, &selected_name, ¶ms, actor)
+ .await?;
+ Ok(ChangeOutput {
+ branch: branch.to_string(),
+ query_name: selected_name,
+ affected_nodes: result.affected_nodes,
+ affected_edges: result.affected_edges,
+ actor_id: actor.map(String::from),
+ })
+ }
+ }
+ }
+
+ /// `query` — run a read query against `target`. Folds `execute_read` /
+ /// `execute_read_remote`; the embedded arm opens WITHOUT policy (reads
+ /// never attach one), so this verb resolves via `resolve()`.
+ pub(crate) async fn query(
+ &self,
+ target: ReadTarget,
+ query_source: &str,
+ query_name: Option<&str>,
+ params_json: Option<&Value>,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ let (branch, snapshot) = match &target {
+ ReadTarget::Branch(branch) => (Some(branch.clone()), None),
+ ReadTarget::Snapshot(snapshot) => (None, Some(snapshot.as_str().to_string())),
+ };
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["read"], &[])?,
+ Some(serde_json::to_value(ReadRequest {
+ query_source: query_source.to_string(),
+ query_name: query_name.map(ToOwned::to_owned),
+ params: params_json.cloned(),
+ branch,
+ snapshot,
+ })?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, .. } => {
+ let (selected_name, query_params) = select_named_query(query_source, query_name)?;
+ let params = query_params_from_json(&query_params, params_json)?;
+ let db = Self::open_embedded(uri).await?;
+ let result = db
+ .query(target.clone(), query_source, &selected_name, ¶ms)
+ .await?;
+ Ok(read_output(selected_name, &target, result))
+ }
+ }
+ }
+
+ /// `invoke_named` — run a stored query **by catalog name** (RFC-011 D3).
+ /// Served-only: the catalog is server-owned, so a `--store` (embedded)
+ /// scope has nothing to resolve the name against. `expect_mutation` carries
+ /// the verb's asserted kind; the server rejects a mismatch (400) before
+ /// running, so the response is exactly the expected envelope — the caller
+ /// deserializes it as the concrete `T` (`ReadOutput` for `query`,
+ /// `ChangeOutput` for `mutate`), sidestepping the untagged wire enum.
+ pub(crate) async fn invoke_named(
+ &self,
+ name: &str,
+ expect_mutation: bool,
+ params_json: Option<&Value>,
+ branch: Option,
+ snapshot: Option,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ let body = InvokeStoredQueryRequest {
+ params: params_json.cloned(),
+ branch,
+ snapshot,
+ expect_mutation: Some(expect_mutation),
+ };
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["queries", name], &[])?,
+ Some(serde_json::to_value(body)?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { .. } => bail!(
+ "by-name invocation needs a server (the stored-query catalog is \
+ server-owned); use -e '' or --query for an ad-hoc query \
+ against --store, or address a server with --server / --profile"
+ ),
+ }
+ }
+
+ pub(crate) async fn branch_create_from(
+ &self,
+ from: &str,
+ name: &str,
+ ) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["branches"], &[])?,
+ Some(serde_json::to_value(BranchCreateRequest {
+ from: Some(from.to_string()),
+ name: name.to_string(),
+ })?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let actor = actor.as_deref();
+ db.branch_create_from_as(ReadTarget::branch(from), name, actor)
+ .await?;
+ Ok(BranchCreateOutput {
+ uri: uri.clone(),
+ from: from.to_string(),
+ name: name.to_string(),
+ actor_id: actor.map(String::from),
+ })
+ }
+ }
+ }
+
+ pub(crate) async fn branch_delete(&self, name: &str) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::DELETE,
+ remote_url(base_url, &["branches", name], &[])?,
+ None,
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let actor = actor.as_deref();
+ db.branch_delete_as(name, actor).await?;
+ Ok(BranchDeleteOutput {
+ uri: uri.clone(),
+ name: name.to_string(),
+ actor_id: actor.map(String::from),
+ })
+ }
+ }
+ }
+
+ pub(crate) async fn branch_merge(&self, source: &str, into: &str) -> Result {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ remote_json(
+ http,
+ Method::POST,
+ remote_url(base_url, &["branches", "merge"], &[])?,
+ Some(serde_json::to_value(BranchMergeRequest {
+ source: source.to_string(),
+ target: Some(into.to_string()),
+ })?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let actor = actor.as_deref();
+ let outcome = db.branch_merge_as(source, into, actor).await?;
+ Ok(BranchMergeOutput {
+ source: source.to_string(),
+ target: into.to_string(),
+ outcome: outcome.into(),
+ actor_id: actor.map(String::from),
+ })
+ }
+ }
+ }
+
+ /// `apply_schema` — apply `schema_source`. The embedded arm runs the
+ /// caller's catalog validator (stored-query registry check) inside the
+ /// engine's `apply_schema_as_with_catalog_check`; the remote arm runs
+ /// the server's own check and IGNORES `validate`. The `impl FnOnce`
+ /// validator is exactly why this is an enum, not a trait (non-object-
+ /// safe).
+ pub(crate) async fn apply_schema(
+ &self,
+ schema_source: &str,
+ allow_data_loss: bool,
+ validate: F,
+ ) -> Result
+ where
+ F: FnOnce(&Catalog) -> omnigraph::error::Result<()>,
+ {
+ match self {
+ GraphClient::Remote {
+ http,
+ base_url,
+ token,
+ } => {
+ // MR-694 PR B: SchemaApplyRequest carries allow_data_loss so
+ // Hard-mode drops are no longer CLI-only; the server's
+ // `server_schema_apply` honors it (and runs its own catalog
+ // check, so `validate` does not apply here).
+ remote_json::(
+ http,
+ Method::POST,
+ remote_url(base_url, &["schema", "apply"], &[])?,
+ Some(serde_json::to_value(SchemaApplyRequest {
+ schema_source: schema_source.to_string(),
+ allow_data_loss,
+ })?),
+ token.as_deref(),
+ )
+ .await
+ }
+ GraphClient::Embedded { uri, actor } => {
+ let db = Self::open_embedded(uri).await?;
+ let result = db
+ .apply_schema_as_with_catalog_check(
+ schema_source,
+ omnigraph::db::SchemaApplyOptions { allow_data_loss },
+ actor.as_deref(),
+ validate,
+ )
+ .await?;
+ Ok(schema_apply_output(uri, result))
+ }
+ }
+ }
+
+ /// `export` — stream the branch as JSONL into `writer`. The streaming
+ /// shape (a `W: Write`, not a returned DTO) is why this lands in 3c
+ /// rather than 3b. Opens WITHOUT policy (like reads), so it is reached
+ /// via `resolve()`; the Embedded arm opens bare. The Remote arm streams
+ /// the chunked response body straight through (no buffering the whole
+ /// export in memory).
+ pub(crate) async fn export