sentin8-sdk — The Sentin8 Shared Library Workspace (SDD §2.1)
Summary
One cargo workspace holding the platform's shared-plane crates: the core
layer (errors, crypto, config, build-info, idempotency, registration, rfw,
openapi, middleware, service-discovery), the identity layer (jwt, fapi,
m2m, auth-client), the TLS plane, the bus plane (contracts, client, membus,
outbox), the Shield policy engine, the service shell + audit, and the
sentin8 facade. Every platform and vertical repo consumes it as a single
pinned git dependency (tag = "sdk-vX.Y.Z").
Service-scoped SDK crates ({svc}-client/{svc}-contracts) do not
live here — each lives in its owning service repo behind a per-service
mini-facade (sdk/ directory, package sentin8-{svc}-sdk). The
stay/move boundary is shared-plane linkage: a crate stays in the SDK iff
some shared-plane crate links it (shell links auth-client,
bus-client, outbox; shield links auth-client, m2m, the bus
plane; middleware links tls).
Motivation
- One-repo dev story: a service team clones one repo and builds — the
SDK arrives as a pinned git dependency; no platform checkout, no live
database (committed
.sqlxcaches). - No dependency-direction defects: the shared plane links no
service-scoped client. Telemetry (policy decisions, service metrics)
publishes through MemBus (
stat:telemetryPub/Sub family) instead of an HTTP stat client — the{leaf}-policyM2M workloads are gone. A service's recurring internal work is declared at enroll time (schedulesregistration section) or runs on an internal timer — never via boot-time cross-service self-registration. - Compile-scope honesty: the facade re-exports only the shared plane; a consumer compiles the service SDKs it actually pins, nothing more.
- No legacy naming: no hand-rolled facade clients, no stale module aliases, vendor names only in the manifest mapping (Shield is the engine's only name).
Architecture & High-Level Design
What is deliberately NOT here: service binaries, service-scoped
HTTP clients/contracts ({svc}-client/{svc}-contracts — owner repos),
registration files (each repo ships its own), vertical knowledge of any
kind.
Key Traits and Data Structures
Sentin8Config(core/config): the single source of truth for runtime derivation — every domain/URL derives fromSENTIN8_DOMAIN; Valkey and Bus URLs, pool caps (pg_pool).sentin8_config::pg_pool::connectis the fleet's ONE PostgreSQL connection factory: it enforces the pool cap AND the certificate- identity TLS plane (group DESIGN.md §3.2 —sslmode=verify-fullagainst the published Acme CA chain, the workload's m2m client pair, and a hard rejection of password-bearing DSNs; there are no database passwords anywhere).Registration+ScheduleDecl+EventTriggerDecl(core/registration): the declarative self-description surface a service emits via--print-registration;schedulesprovisionscron.schedulesat enroll time.shield(sentin8-shield): the declarative policy engine (RBAC + ABAC- ReBAC). Decision events and service metrics publish to MemBus
(
stat.telemetry), fire-and-forget, log-only on failure.
- ReBAC). Decision events and service metrics publish to MemBus
(
shell(sentin8-shell): the canonical boot sequence (10 steps) every service runs — TLS acceptor, auth client, Shield boot, outbox relay, audit push loop.membus(sentin8-membus): embedded Valkey client for MemBus Pub/Sub (ephemeral, newest-wins — telemetry class) and Streams (durable, consumer groups). No HTTP, no auth — tenant isolation by key derivation.- facade
sentin8:use sentin8::…for the shared plane only (sentin8::shell,sentin8::shield,sentin8::auth::…,sentin8::tls::…,sentin8::bus::…).
Rationale, Trade-offs, and Alternatives
- Why one workspace, not per-crate repos: the facade depends on every shared crate; a whole-workspace tag keeps one version ring and one pin. Alternative — per-crate repos with independent semver — rejected: pin combinatorics across 90 consumer repos, no atomic cross-layer change.
- Why service crates moved out (SDD): the facade-for-all pin made every repo compile all 40 crates; contract changes spun the global ring for repos that never imported them. Owner-repo hosting gives per-service version rings and honest compile scope. Rejected alternative — feature-gated facade — keeps one ring and a facade that knows about services (SDD §2 placement discussion).
- Why MemBus for telemetry: the shared plane cannot link a service-scoped client without inverting the DAG; telemetry is fire-and-forget by contract (loss on outage is acceptable — "non-important updates"), so the ephemeral Pub/Sub class fits exactly. Rejected — durable Streams for telemetry — would impose DLQ/retry semantics on a loss-tolerant class and hold unbounded stream entries.
- Why boot-time self-registration was removed: acme (tier 1) and auth (tier 2) registered cron schedules before cron (tier 4) existed — the registration could only ever land on the next boot; and a root service depending on cron inverts the dependency pyramid (everything depends on auth; auth must not depend on anything tier-4).
- Dispatching: MemBus uses Valkey GLIDE's typed client surface with a dedicated pub/sub connection for subscribers — the connection invariant documented in membus DESIGN.
- Concurrency: shield's telemetry emitter is a
OnceLock-held client replaced by aOnceLock-heldEnvBuspublish path — same init-once-then-fire pattern, no locks on the emit path.
Internal Implementation & Invariants (Private Design)
- SDD-1 — crate placement by shared-plane linkage, not naming.
auth-clientstays (shell/shield link it);bus-client/outbox/bus-contractsstay (shell links them). If a future shared-plane crate needs a service client, that is a design defect — publish a domain event or MemBus frame instead. - SDD-6 — the facade never re-exports a mini-facade (would invert the DAG — the SDK repo would pin service repos).
- Envelope discipline (membus): tenant identity is a property of the
key (
mbus:{env}:…/mchan:{env}:…), never of the payload. - sqlx offline: the workspace carries a committed
.sqlx/cache; consumer builds need no live PG.
Unresolved Questions / Future Designs
- CI pin-freshness automation for per-service
{svc}-sdk-vX.Y.Ztags — aggregation-CI territory (SDD §7). - Whether
membusgains a frame-schema registry for payload validation at decode time — deferred until more families exist.
Design attribution: sentin8 and its underlying concepts were designed by Hamid Jahanzad Hamid@Jahanzad.com.