Skip to content

NoKV 0.20.0

Latest

Choose a tag to compare

@github-actions github-actions released this 31 Oct 15:17
· 6 commits to main since this release
6e8a84e

NoKV 0.20.0 lets many agents share a deployment safely. An owner authenticates every client over TLS 1.3, authorizes every request by the role its principal holds on the root, records who began every operation, and logs every refusal. Agents no longer write to the object store, the Python SDK follows the native CLI contract, and a single-host deployment refuses what it cannot trust before anything changes.

Highlights

  • Tenants, principals, and roles. The control plane stores tenants, principals, certificate credentials, and tenant- or root-scoped roles, managed with nokv identity (#573). An owner that serves workspace RPC over TLS 1.3 admits only a current credential of the tenant that owns its roots (#575). It authorizes every request as reader, writer, or operator (#578). Revoking a credential or a role takes effect within about a second, on open connections too. A root that a tenant owns is served only over TLS (#580).
  • Every operation names its initiator. Workspace format 15 records the principal that began every durable operation, metadata command, and change event (#581). Only that principal or an operator may continue, abort, or complete it (#582, #620). operation status and change events report the initiator.
  • Refusals are named and logged. CLI contract v2 adds unauthenticated (exit 10) and permission_denied (exit 11) (#583). An append or a multi-stage publish that the owner refuses now reports that refusal, not an unknown outcome (#611). nokv serve --audit-log PATH appends one JSON line for every refused connection and every denied request (#619).
  • Agents hold read-only object credentials. The owner writes every object block that a client hands it, after checking the block against the staged record (#586). It does not hold the shard's write gate while it writes to the provider (#592). Object credentials lists the keys and example policies for each role (#591).
  • etcd with mutual TLS and least-privilege roles. Each owner, Agent, and operator can authenticate to etcd with its own certificate and be bounded to the keys its role needs (#606), from the CLI, the Rust SDK, and the Python SDK (#610).
  • One session, many roots. A Rust Session (#607) and a Python Session with TLS (#609) share one transport, one route cache, and one set of credentials across roots. An owner serves a root provisioned after it started, without a restart (#608). nokv provision --workbench-root binds each root's Workbench root once, and Agent commands read it from the control plane (#579).
  • The native CLI contract, end to end.
    • Every failure prints one JSON line with a class and a next action, and exits with that class's code (#557).
    • Writes can carry a caller operation id, and paths and operations can be queried (#557).
    • workbench_put_file, workbench_edit, and workbench_append replay under a caller operation id (#600).
    • Retries can back off (--retry-backoff-ms, --max-retry-backoff-ms) (#602), and wait out an attempt that an owner abandoned in a restart (#604).
    • The Python SDK reports the same error_class and next_action (#577).
  • Single-host deployments fail closed.
    • A control directory is created 0700, with files 0600.
    • A directory that group or other users can write, that another user owns, or that NoKV cannot write is refused as operator_required before anything changes (#618, #623). A running owner that meets that refusal exits, naming the path and the fix.
    • A malformed --etcd-endpoint is invalid_input before any connection. One that carries credentials is refused without repeating them (#623).
    • --advertise-endpoint must be an IP address and port, because clients do not resolve host names in routes (#625).
  • Holt 0.8.8. When a metadata batch fails after Holt has changed cached pages, Holt now latches the store closed, so no checkpoint can write half of the batch (#624). In acceptance, one owner lifecycle in ten hit this as a shard that refused to reopen.
  • Security fixes.
    • Local collect, materialize, and the Python file adapters resist path replacement (#584).
    • Materialized files are published only once complete (#590).
    • The torch Distributed Checkpoint adapter no longer loads pickled data (#585).
  • Removed: the deprecated nokv mcp sidecar (#605). The native CLI is the integration surface, then the Python SDK, then the Rust SDK.

Performance

Measured on one Apple M3 Pro laptop with release builds, during the tenant-identity acceptance and the pre-shipment inspection of this release:

  • Authorization decision: 61 to 115 ns per request (64 principals, each with one tenant role and eight root roles).
  • TLS 1.3 handshake and authorization: about 0.7 ms more per CLI process, measured against a direct plaintext connection with 30 alternating rounds.
  • Revocation: a revoked credential or role is refused within about a second (20 samples each, p50 0.96 to 0.99 s).
  • Owner-mediated uploads, compared with the previous direct Agent upload:
    • put of 1, 16, and 64 MiB takes 0.34×, 0.83×, and 1.15× the time; the 1 MiB case gains because the per-process provider check is gone, and the 64 MiB case pays one more hop;
    • reads take 1.07× to 1.20× the time.
  • Many agents on the Pi harness, each with its own principal over TLS:
    • 16 agents on one root ran 49.6 turns/s with a turn p99 of 406 ms;
    • 64 agents on one root ran 50.5 turns/s against 61.1 with a root each, and the shared root's p95 and p99 were 60 to 67% higher.

The 0.12.0 W1/W2 release check against SQLite was not rerun.

Breaking changes: upgrade clients, the CLI, the SDKs, and owners together

  • Wire protocol. The workspace RPC schema is nokv.workspace.rpc.v15. Its handshake refuses 0.12.x clients and owners.
  • Storage format. Metadata is stored in workspace format 15, which records initiators. A 0.20.0 owner refuses to open a format-14 store written by 0.12.x, and there is no migration. Start 0.20.0 owners on a new metadata store.
  • CLI contract v2 (nokv.cli.contract.v2):
    • a refused credential exits 10 (unauthenticated), and a refused role or continuation exits 11 (permission_denied);
    • an unsafe or unusable control directory exits 9 (operator_required);
    • a malformed --etcd-endpoint exits 2. That includes a bare host:port without http://, an uppercase scheme, and port 0.
  • Object credentials. Owners write every object, so they need write access under their prefix. Agents need only read access. The Agent-side provider admission check and its receipts are gone.
  • Command line.
    • --workbench-root is given once to nokv provision; Agent commands no longer take it.
    • Over TLS, Agent commands no longer need --agent-id.
    • A root bound to a tenant is not served without TLS.
    • --advertise-endpoint must be IP:PORT.
    • nokv mcp is an unknown command.
  • Change feed. A ReadChanges page holds at most 64 events (#601).
  • Python SDK.
    • Exceptions carry error_class and next_action.
    • RoutingConfig.etcd checks its endpoints when it is constructed.
    • The torch extra needs msgpack.
    • Checkpoints that the torch adapter saved as pickles cannot be loaded; re-save them from a trusted original state.
  • Building from source needs Rust 1.91 or later (#536).

Upgrade from 0.12.x

  1. Stop the owners and start 0.20.0 owners on new metadata stores. Upgrade the CLI and the SDKs at the same time.
  2. Give owners object write credentials, and narrow Agents to read-only. See object credentials.
  3. Provision each root once with --workbench-root.
  4. To authenticate clients:
    • create tenants, principals, credentials, and roles with nokv identity;
    • serve workspace RPC with TLS;
    • optionally, put etcd behind mutual TLS with the roles in control-plane credentials.
  5. On a single host, run every process that uses a control directory as the directory's owner. The directory must not be writable by group or others.

Release tooling

  • Container images carry OCI and Artifact Hub metadata on the image index and on every platform manifest, and the Docker Image workflow checks it on every pull request (#622). The package page is docs/container-image.md.
  • Dependabot version updates are checked for their title only (#613), and pull request descriptions follow one eight-section format (#563).
  • The Python SDK contract runs in CI next to the native CLI contract (#570). The live gates drive the product through the native CLI (#587, #588, #589).

Install

Homebrew (macOS):

brew install NoKV-Lab/tap/nokv     # or: brew update && brew upgrade nokv
nokv version --json

Python SDK (abi3 wheels for Linux x86_64/aarch64 and macOS arm64/x86_64, CPython 3.9+):

pip install "nokv==0.20.0" \
  --find-links "https://github.com/NoKV-Lab/NoKV/releases/expanded_assets/v0.20.0"

From source (Rust 1.91 or later):

cargo build --release -p nokv --bin nokv

Pull requests since 0.12.1

  • #536 deps: upgrade opendal to 0.59.5 with the 0.59 safety audit
  • #556 fix: replay a request id by its operation after an owner restart
  • #557 feat: add typed failures, caller-identified put, path stat, and operation results
  • #561 docs: rerun the README evidence snapshot against v0.12.1
  • #562 test: pin the claim term of the inline receipt check
  • #563 chore: enforce concise PR descriptions
  • #564 perf: admit the object provider only at the first object write
  • #567 docs: record the tenant identity and RBAC direction
  • #569 deps: batch the pending Dependabot bumps
  • #570 test: qualify the direct Python SDK contract in CI
  • #571 test: make typed preflight tests deterministic
  • #573 feat: store tenant identity records in the control plane
  • #574 test: make the snapshot read view test independent of test order
  • #575 feat: authenticate workspace RPC clients over TLS 1.3
  • #577 feat: bring the Python SDK to the native CLI contract
  • #578 feat: authorize workspace RPC requests by tenant role
  • #579 feat: bind each root's Workbench root at provision
  • #580 feat: harden RPC authorization at the owner
  • #581 feat: record the initiating principal in workspace format 15
  • #582 feat: continue an operation only as its initiator or an operator
  • #583 feat: CLI contract v2 names a refused credential or role
  • #584 fix: secure local file transfer paths
  • #585 fix: reject executable DCP checkpoint formats
  • #586 feat: owner writes every object block the client hands it
  • #587 test: drive the object namespace gate through the native CLI
  • #588 test: drive the live Workbench gate through the native CLI
  • #589 test: drive the restore composition gate through the native CLI
  • #590 fix: keep local file publication atomic
  • #591 docs: document object store credentials for owners and Agents
  • #592 perf: keep provider writes out of the shard write gate
  • #593 chore: retire the multipart upload token from the staged ledger
  • #594 ci: bump actions/setup-python from 7.0.0 to 7.1.0
  • #595 deps: bump rustls from 0.23.40 to 0.23.46
  • #596 deps: bump tokio from 1.53.3 to 1.54.1
  • #598 deps: bump getrandom from 0.3.4 to 0.4.2
  • #599 deps: bump libc from 0.2.190 to 0.2.191
  • #600 feat: let the Workbench mutation tools replay under a caller operation id
  • #601 fix: cap change-feed pages at 64 events
  • #602 feat: bounded retry backoff for client requests
  • #603 fix: wait for a serving owner on a fresh port in the object namespace gate
  • #604 feat: wait out an orphaned attempt under the caller's identity
  • #605 chore: remove the deprecated nokv mcp sidecar
  • #606 feat: authenticate etcd clients with mutual TLS and least-privilege roles
  • #607 feat: derive per-root clients from one shared session
  • #608 feat: serve roots provisioned after the owner started
  • #609 feat: connect the Python SDK over TLS from one session with the bound Workbench root
  • #610 feat: connect the Python SDK to etcd over mutual TLS
  • #611 fix: report a refused append or publish as the refusal, not an unknown outcome
  • #612 test: gate the security negative matrix, a principal race, and the threat model
  • #613 ci: check Dependabot version-update PRs for the title only
  • #614 test: refuse the unbound root before binding it in the late-root mount scenario
  • #615 fix: report a provider admission outage as transient and repeat it within the retry budget
  • #616 deps: bump rustix from 1.1.4 to 1.1.6
  • #617 deps: bump tonic from 0.14.6 to 0.14.8
  • #618 fix: refuse control directories other users can write
  • #619 feat: append every refusal of a TLS owner to an audit log
  • #620 fix: refuse another principal's retry of an append's cleanup
  • #621 test: pin a commit's projection binding against other principals
  • #622 ci: publish NoKV images with Artifact Hub metadata
  • #623 fix: report control-directory refusals as operator_required before anything changes
  • #624 fix: pin Holt 0.8.8 so a failed metadata batch cannot become partly durable
  • #625 fix: refuse an advertised owner endpoint that clients cannot connect to
  • #626 release: NoKV 0.20.0