kapable-ops-sdk (0.1.2)
Installation
[registries.forgejo]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add kapable-ops-sdk@0.1.2 --registry forgejoAbout this package
kapable-ops-sdk
Operator-tier Rust SDK for the Kapable v2 platform — the trust-perimeter twin
of the customer kapable-sdk crate, and the Rust mirror of
@kapable/ops-sdk.
Version 0.1.0 — 10 operator modules, 111 Rust methods, kept at exact
method parity with the TypeScript package (enforced by the TS
test/parity.test.ts, which parses both languages' real source). Published to
the Kapable Forgejo cargo registry (2026-06-09).
Install
One-time registry setup in .cargo/config.toml (project or ~/.cargo):
[registries.kapable]
index = "sparse+https://git.kapable.dev/api/packages/kapable/cargo/"
Then:
[dependencies]
kapable-ops-sdk = { registry = "kapable", version = "0.1" }
It wraps the operator / admin / service-token endpoints the customer SDK
deliberately excludes — Berth, Foreman, kapable-host, kapable-gateway,
harbor-admin, the kapable-data query proxy, Burrow tunnels, kapable-auth admin,
and the billing/comms internal routes — mirroring the kapable (customer) vs
kapable-ops (operator) CLI trust split.
The operator vs customer perimeter (the load-bearing invariant)
Rust constructors can't throw, so the operator/customer boundary is enforced via
a fallible constructor: OpsClient::new(base_url, token) returns
Result<Self, OpsError> and errors with OpsError::CustomerKeyRejected when
handed a customer API key. An operator wrapper therefore cannot even be
instantiated with a customer credential.
| Token prefixes | |
|---|---|
| Accepted | kses_, sk_admin_, st_/st_ci_/st_deploy_/st_wh_/st_svc_, unrecognized/JWT-shaped |
| Rejected | sk_live_ / sk_test_ / sk_org_ |
The SDK never enforces authorization — the server is the sole authority. Some
modules layer an additional per-call header credential (Berth deploy-sync's
Ed25519 X-Signature; billing/comms X-Internal-Token; auth admin x-api-key).
use kapable_ops_sdk::OpsClient;
use kapable_ops_sdk::data::QueryOptions;
# async fn example() -> Result<(), kapable_ops_sdk::OpsError> {
let ops = OpsClient::new("https://data.kapable.ai", "st_ci_...")?;
// data query proxy
let r = ops.data().query("kapable_platform", "SELECT id FROM orgs WHERE slug = $1",
QueryOptions { params: vec![serde_json::json!("acme")], ..Default::default() }).await?;
println!("{} rows", r.row_count);
// Berth deploy control-plane
let berth = OpsClient::new("http://localhost:3100", "st_svc_...")?;
let binaries = berth.berth().list_binaries().await?;
// A customer key errors at construction:
assert!(OpsClient::new("https://data.kapable.ai", "sk_live_...").is_err());
# Ok(())
# }
Modules
Each module is reachable as ops.<module>().<method>(). Method names mirror the
TS package (snake_case), and every method wraps the identical VERB path as
its TS twin (asserted by the parity test).
| Module | Methods | Service | Operator surface |
|---|---|---|---|
auth |
14 | kapable-auth |
ADMIN surface (org/app provision, SSO callbacks, beta gate, request metrics, launchpad admin) |
berth |
25 | berth |
Deploy control-plane (binaries, deploy-sync, slots, reconcile, freeze, rollback, restart, canary/rollout) |
billing |
1 | kapable-billing |
Internal billing-portal mint (X-Internal-Token) |
burrow |
4 | burrow |
Tunnel REGISTRY |
comms |
2 | kapable-comms |
Service-to-service internal routes (X-Internal-Token) |
data |
1 | kapable-data |
DB query proxy |
foreman |
15 | foreman |
CI job queue + recipe runner |
gateway |
6 | kapable-gateway |
gateway_services CRUD + reconcile |
harbor |
23 | harbor |
ADMIN/mutation surface (products, features, tiers, licenses, releases, org keypairs) |
host |
20 | kapable-host |
Containers + blue-green deploy, snapshots, images, volumes |
| Total | 111 | 10 services | operator coverage 51.2% |
⚠
harborhas no server-side RBAC and incomplete org-scoping (IDOR, IMP-1366). Safe here only because this client rejects customer keys at construction.
Security types — shared, not copied
Permission / TokenType / Scope / AuthRequirement are re-exported from
the kapable-sdk crate's security module (a local path dependency), so the
two crates share a single source of truth and cannot drift. This crate adds the
operator-perimeter helpers: is_operator_token, OPS_ACCEPTED_TOKEN_TYPES,
OPS_REJECTED_TOKEN_TYPES.
Build & test
cargo build
cargo test # unit + wiremock integration tests + doctests
This crate is standalone (no Cargo workspace in platform/); cargo build
works on its own.
Coverage, manifest, drift, and CHANGELOG
The coverage manifest, drift/phantom check, and per-module/coverage numbers are driven from the TypeScript package (single source of truth across both languages):
- Coverage manifest:
../kapable-ops-sdk/coverage-manifest.json(cd ../kapable-ops-sdk && bun run generate-manifest). - Drift / phantom check:
cd ../kapable-ops-sdk && bun run check-drift. - Cross-language parity test:
../kapable-ops-sdk/test/parity.test.tsparses this crate'ssrc/<mod>/mod.rsand asserts the method set + each method'sVERB pathmatches the TS twin. - CHANGELOG:
../kapable-ops-sdk/CHANGELOG.mdcovers both packages (they ship together at method parity).
Related
../kapable-sdk/CONTRIBUTING.md— shared SDK contribution + coverage-tooling conventions.../docs/sdk-surface-matrix.md— the full per-service surface + RBAC token-tiering matrix.../kapable-ops-sdk— the TypeScript twin.
Dependencies
| ID | Version |
|---|---|
| base64 | ^0.22 |
| chrono | ^0.4 |
| futures-util | ^0.3 |
| kapable-sdk | ^0.8.3 |
| reqwest | ^0.12 |
| serde | ^1 |
| serde_json | ^1 |
| thiserror | ^2 |
| tokio | ^1 |
| uuid | ^1 |
| tokio | ^1 |
| wiremock | ^0.6 |