Roadmap
Atlas follows the phased plan in Zyvor_Ceph_Integration_Developer_Implementation_Plan.pdf
(Β§18 execution plan). This file tracks what's built vs. what's next. Maturity β implemented,
lab-verified, production-supported, experimental, or planned β is the short matrix in
STATUS.md. This file is history; when the two disagree, STATUS.md wins.
π‘ Slice β observe-first storage I/O (this tree)β
atlas-iocrate +atlas-io-agent: fake bio pipeline, device map, histograms, workloads, RCA, leased write-freeze (fail open),/metrics.- BPF C contracts under
crates/atlas-io/bpf/β not compiled in CI. - Next: CO-RE loader, real
block_rq_*attach, Observatory UI, gateway proxy.
β Slice 1 β MVP foundation (done, verified)β
Read-only control plane + real Ceph lab.
atlas-*Cargo workspace; axum 0.8 gateway; SQLite inventory.StorageDrivertrait; real + fake Ceph drivers; live Kubernetes driver.- Read-only REST API (backends, discover, clusters, nodes, osds, pools, volumes, storage-classes, metrics, alerts/jobs stubs).
- Discovery worker β normalized inventory; audit logging.
- Rook Ceph lab manifests (
deploy/rook-ceph-lab/) + single-node overlay. - k3s deployment (
scripts/deploy-remote.sh,deploy/k8s/), fake + real (ceph) images. - Verified end-to-end on real k3s + Rook Ceph: live k8s StorageClass/PV discovery, real RBD PVC provisioning, and Atlas real-driver discovery of the live cluster.
β Slice 2 β Write path & job engine (done, verified)β
- Async job engine (
atlas-jobs) β in-process tokio worker backed bystorage_jobs, PDF Β§10.5 state machine (pendingβqueuedβrunningβverifyingβsucceeded|failed). SQLite-only (no Redis/NATS). POST /volumesβ creates a Ceph-backed PVC;202 Accepted+ job id.DELETE /volumes/{id},POST /volumes/{id}/expand,POST /volumes/{id}/snapshots,DELETE /snapshots/{id}β all async jobs.- Real
GET /jobs,GET /jobs/{id},GET /snapshots,GET /policies. atlas-policyβ intent (production|database|development|shared|ai) β StorageClass + access/volume mode (PDF Β§12.3).atlas-driver-k8swrite ops β create/get/delete/expand PVC + VolumeSnapshot (via DynamicObject).- Idempotency keys on create (PDF Β§17.4); ownership recorded in
product_bindings. atlasctl create-volume|snapshot-volume|delete-volume|jobs|snapshots|policies.- Verified end-to-end on real k3s + Ceph:
POST /volumesβ PVC Bound onzyvor-rbd-prodβ snapshot β VolumeSnapshotreadyToUse=true, all driven through the job engine.
Slice 2 follow-upsβ
- β
Snapshot clone/restore (
POST /snapshots/{id}/clone|restore) β provision a new PVC from a VolumeSnapshot (dataSource), with parent/child dependency tracking (storage_volumes.source_snapshot_id) and a safe-delete guard (409 unless?force=true). Verified on real Ceph. - β
Safe-by-default deletes for production volumes:
DELETE /volumes/{id}?confirm=truerequired for prod/database StorageClasses (PDF Β§14 Rule 2;force=trueaccepted as alias). (Snapshot delete already guards on dependents; volume delete now requires confirmation.) - β WebSocket/SSE job progress for the UI.
- β
Direct RBD create path (bypassing CSI) for non-Kubernetes consumers (machina/libvirt, bare
VMs):
POST /rbd-images {name, size_bytes, pool?}runsrbd createand records a volume row (native idrbd:pool/image, no PVC);GET /rbd-images?pool=lists from Ceph;DELETE /rbd-images/{pool}/{image}runsrbd rm.atlasctl create-rbd-image. Verified live: image created (rbd info1 GiB), listed, and removed on real Ceph. - β
Direct RBD clone (golden image):
POST /rbd-images/{pool}/{image}/clone {name, snap?}snapshots + protects the base andrbd clones a COW copy β the machina/libvirt base-image β per-VM workflow. Verified live: clone'srbd infoshowsparent: <pool>/golden-base@v1. - β
Real usage stats:
POST /rbd-usage/refreshrecomputes each volume'sused_bytesviarbd du(resolving the image from therbd:id or the PVCβPV for CSI volumes). Verified live: a fresh volume's image resolved andused_byteswritten.atlasctl refresh-usage. - β
RBD resize + flatten:
POST /rbd-images/{pool}/{image}/resize {size_bytes}grows an image (updates inventory size);.../flattendetaches a COW clone from its parent snapshot. Verified live: base 1β3 GiB, clone flattened to independent.atlasctl resize-rbd-image/flatten-rbd-image. - β
Volume list filters:
GET /volumes?state=&tenant=. Verified live. - β
Raw-RBD snapshots:
GET/POST /rbd-images/{pool}/{image}/snapshots,POST .../rollback(point-in-time restore, admin). Verified live (create β list[pit1]β rollback). - β
Object-storage observability:
GET /buckets/{id}/stats(radosgw-admin bucket statsβ object count, size, quota) andGET /buckets/{id}/objects[?prefix=](S3ListObjectsV2). Verified./statsnow has a Fake-mode branch (returns a zeroed-but-availablestub) instead of crashing on deployments without aradosgw-adminbinary; the underlyingradosgw-admincall is bounded to 12s so a slow/unresponsive RGW soft-fails (available: false) rather than hanging. - β
Randomized-testing validation fixes β found via a randomized battery of valid/boundary/
invalid API probes:
POST /bucketsnow rejects names under 3 characters (400 VALIDATION_ERROR) matching the S3 bucket-naming rules the console already advertises, instead of silently accepting them (harmless in practice since Rook's OBC always generates a UUID-suffixed real bucket name regardless, but the gap let a caller's mistake through unflagged).atlas_policy::resolvenow rejects an unrecognizedpolicyintent with400 VALIDATION_ERRORinstead of silently falling back to the request kind's default placement β a typo inpolicyused to provision storage silently rather than failing loudly; a tenant-specific policy override (PUT /tenants/{id}/policies/{intent}, not restricted to the built-in catalog) still resolves correctly.
β Web dashboard β Storage Center (Zeus OS-style React console)β
A full React 19 + Vite + Tailwind + TanStack Query + zustand SPA at crates/atlas-gateway/ui,
built to match the Zeus OS (v9s) look-and-feel β vendored HSL design tokens + tahoe/zeus
glass/pill/badge/table classes, the mac-desktop shell (glassy menu bar with cluster-health badge,
clock, spotlight βK, live running-jobs indicator, and a Bearer-token control; grouped "Center"
sidebar; app dock), Inter type, Lucide icons, recharts. Built by a node:22 Docker stage and
embedded into the gateway binary via rust-embed, served at / with an SPA fallback (open
http://<node>:30511/). Local dev: make ui (bundle) / make ui-dev (Vite proxy).
Every Atlas capability is wired: Overview (capacity gauge, health, client-I/O sparkline,
recovery, pools/OSDs, open alerts); Volumes (filters, create/expand/snapshot/schedule/delete +
detail drawer with labels & product bindings); RBD Images (create/clone/resize/flatten/delete,
snapshots list/create/rollback, usage refresh); Snapshots, Schedules, Backups
(create/restore/download/delete), Buckets (create/stats/object-browser/delete), Alerts
(state filter + evaluate), Metrics (Ceph, prefix filter), Jobs (live SSE progress),
Audit (filters), Tenants (quota + policy CRUD), Access (mint service-account JWTs),
Policies, Backends, Kubernetes, Cluster. Write actions thread 202 β SSE watch β toast β refetch. Replaces the earlier vanilla-JS ui.html. This standalone console sidesteps the
Zeus OS atlas/ZeusStorageCenter.tsx collisions; Zeus OS can later embed it or call the same API.
UX polish: a branded login gate (Zyvor hexagon mark, animated gradient orbs, optional service-account token, live gateway health/version) with a session gate + sign-out; a theme toggle (dark β Aurora neon variant); confirm dialogs on destructive actions; loading/empty states with CTAs; a running-jobs popover; βK spotlight with arrow-nav; copy-to-clipboard IDs; a capacity radial + per-pool utilization bars on the Command Deck; per-page titles, hexagon favicon, responsive sidebar.
- β
Nav overflow-menu fix β the "More sections"/"More shortcuts" fold-menus in the top nav (shown
when the rail is too narrow to fit every section/shortcut) opened but rendered invisible: they used
position: absoluteinside.at-rail-nav/.at-controls, bothoverflow: hidden, so the panel was clipped before it painted. Fixed by portaling both todocument.bodywithposition: fixed+getBoundingClientRect()-computed coordinates, matching the pattern the section flyouts already used. - β
Two-shell Apple shop redesign β Carbon (dark shop: black canvas + Apple Blue) and
Apple Lite (light shop:
#F5F5F7+ Apple Blue). Elevated rounded boxes, SF system type, selection tiles, and swipe rails. Cosmic Orange reserved for the brand mark only. Seedocs/ATLAS_UI_CONTRACT.md. Preceded by an earlier Zeus two-shell port and sidebar chrome work; cascade-layer fixes for shell overrides remain in place.
HTTPS: an optional TLS listener (ATLAS_HTTPS_ADDR + ATLAS_TLS_CERT/ATLAS_TLS_KEY PEM), served
alongside HTTP via axum-server + rustls (ring provider β no cmake in the build). The ceph deployment
mounts a self-signed atlas-tls Secret and exposes NodePort 30543 (https://<node>:30543/; a real
cert/ingress gives trusted TLS).
- β
ATLAS_DISABLE_HTTP: found via the bank production-readiness audit β the plain HTTP listener always ran even when HTTPS was configured, so TLS could be bypassed entirely by hitting the HTTP port directly. This env var skips binding it;validate_for_start()refuses to boot with it set unlessATLAS_HTTPS_ADDR/ATLAS_TLS_CERT/ATLAS_TLS_KEYare all also configured (otherwise there'd be no REST listener at all). Left unset in the shipped manifests for now β flipping it on for a real deployment is a decision to make once HTTPS-only access is confirmed working end-to-end for every client, not an automatic default. - β
gRPC TLS: the gRPC edge previously had no TLS option at all β plaintext regardless of how
REST was configured. It now reuses the same cert/key as the REST HTTPS listener automatically
(
tonic'stlsfeature,Identity::from_pem+ServerTlsConfig) whenever both are configured; stays plaintext otherwise (unchanged default). Verified live on the real-Ceph gateway (which already carries a real self-signed cert/key pair):atlas-gateway gRPC (TLS) listening on 0.0.0.0:5111alongside REST HTTP/HTTPS, all three healthy. This same deploy pass also caught and fixed a real self-state-backup misconfiguration on that gateway (wrong RGW endpoint port β it was pointed at the presigned-URL-only service instead of the plain RGW NodePort, so uploads 400'd); it now uploads successfully there too. - β
Durable gateway DB β the SQLite file is now backed by a
ReadWriteOncePVC (Recreatestrategy). The real-mode gateway dogfoodszyvor-rbd-prod(Ceph); the fake-mode gateway uses the cluster default StorageClass so it still deploys without Ceph. Verified: inventory survives a pod restart. (For multi-replica HA, move to Postgres β the sqlx layer already abstracts this.)
β Slice 3 (part 1) β RGW object storage + backups (done, verified)β
atlas-driver-rgwβ minimal S3 client for RGW (rusty-s3SigV4 signing +reqwest).- Buckets via ObjectBucketClaim:
POST /buckets(async job) creates an OBC; Rook provisions the bucket + Secret + ConfigMap; Atlas records the endpoint + a secret reference (never the keys).GET /buckets,GET /buckets/{id}. - Backups:
POST /backup-jobssnapshots the volume, writes a backup manifest (PDF Β§16.2) to the RGW bucket over S3, reads it back to verify (sha256 checksum).GET /backups,GET /backups/{id}. - Credentials are read from the OBC Secret in-cluster by the job and never logged.
- migration 0003 (
storage_buckets,storage_backups); single-node RGW object store + bucket SC. - Verified on real Ceph RGW: bucket bound, manifest object written + verified, independently
confirmed via
radosgw-admin bucket list.
RGW/backup follow-upsβ
- β
Full data backup (
POST /backup-jobs {"mode":"data"}) β resolves the PVC's real RBD image (PV csi attributes),rbd snap create+rbd export-diff(capped 512 MiB), uploads the diff to RGW alongside the manifest. Verified on real Ceph (data object present, checksum recorded). - β
Restore-from-data (
POST /restore-jobs {"mode":"data"}) β creates an empty PVC, resolves its RBD image, downloads the diff from S3 (checksum-verified), andrbd import-diffs it into the new image. Verified byte-for-byte on real Ceph: wrote a file to volume A β data backup β restore to a new volume B β mounted B β the file read back identical. - β
Multipart streaming β data backup/restore no longer buffer the whole image. Backup streams
rbd export-diffstraight into an S3 multipart upload (16 MiB parts); restore streams the S3 object intorbd import-diffstdin. sha256 is computed over the stream, so there is no in-memory size cap. Verified live: a 20 MiB random file β ~23 MiB diff (2 parts) β restore byte-identical (data_verified: true, source/restored sha256 match). - β
POST /restore-jobsβ reads + checksum-verifies the backup manifest from RGW, then provisions a new PVC from the backup's VolumeSnapshot (PDF Β§16, DR-2). Verified on real Ceph. - β
Backup delete (
DELETE /backups/{id}) β removes the S3 manifest +.rbd-diffdata objects (idempotent) and the RBD snapshot (best-effort), then the row. Verified live (RGW objects β 0, row 404). - β
Retention (count) β
ATLAS_BACKUP_KEEP(0=unlimited) + per-requestkeep; on backup create, prunes backups beyond the keep count (most-recent-first) via delete jobs. Verified live (3 β keep=2 β 2). - β
Retention (age) β
ATLAS_BACKUP_MAX_AGE_SECS(0=disabled) + per-request/CLImax_age_secs; on backup create, prunes completed backups for the volume older than the cutoff via delete jobs.GET /backups?volume_id=filters by volume. Verified live (backup aged past a 3s cutoff β pruned). - β
Bucket delete (
DELETE /buckets/{id}) β deletes the OBC (Rook releases the bucket) + row; blocked with409if backups still reference the bucket, unless?force=true. Verified live (OBC NotFound, row 404). - β
Presigned download (
GET /backups/{id}/download?what=data|manifest) β returns a time-limited SigV4 URL signed with the bucket's in-cluster credentials (never exposed), so a client pulls the object straight from RGW. Verified live (credential-free download β 200, realrbd diff v1bytes). - β
Off-cluster downloads β
ATLAS_RGW_PUBLIC_ENDPOINTmakes the gateway sign presigned URLs against a public RGW host (exposed via theatlas-rgw-publicNodePort, 30513) so they resolve outside the cluster. Verified live (URL host = node:30513, credential-free GET β 200,rbd diff v1). - β
Bucket quotas β
POST /bucketsacceptsmax_objects/max_size; the job sets the OBCadditionalConfigand enforces the RGW per-bucket quota directly viaradosgw-admin(quota set+enable). Verified live (enabled: true, max_objects: 100, max_size: 1 GiB). - Bucket lifecycle policies; time-based backup retention; external RGW ingress for downloads.
β gRPC edge (done, verified)β
tonic0.12 serviceatlas.v1.AtlasStorageserved alongside REST onATLAS_GRPC_ADDR(default:5111):Health,ListClusters/Pools/Volumes,GetVolume,CreateVolume(β job),GetJob,ListAlerts. Server reflection enabled (grpcurl works without the proto).- proto at
crates/atlas-gateway/proto/atlas.proto; built with systemprotoc(installed in the Docker builders). Integration test drives the server with the generated client. - Verified on the cluster with
grpcurl(NodePort 30512):ListPoolsreturned the real Ceph pools over gRPC via reflection. - β
JWT auth β a tonic interceptor gated by
ATLAS_AUTH_REQUIREDverifies an HS256 Bearer token in theauthorizationmetadata with the shared secret (mirrors REST) and injects the actor. Verified live: no token βUnauthenticated, valid token β OK. - β
RBAC β role hierarchy
viewer < operator < admin(PDF Β§14.2;product.service.*= operator). Enforced on both edges (only whenauth_required): writes need operator; delete-volume / create-backend / force-snapshot-delete need admin. Verified live (401/403/202/admin-gated). - β
WatchJob server-streaming RPC β emits the job on each state change until terminal (verified
live:
CreateVolumeβWatchJobstreamed tosucceeded). - β
REST SSE parity:
GET /jobs/{id}/watchstreamstext/event-streamjob frames until terminal (verified live: running β succeeded). - β
Product-integration surface:
CreateVolumetakes anOwner {product, resource_type, resource_id, role}recorded inproduct_bindings;ListVolumesByOwner(product, resource_id?)lets a product enumerate only the volumes it owns;CreateSnapshot(operator) andDeleteVolume(admin) return job replies. Verified live over gRPC reflection (Veyron flow: create-owned β list-by-owner scoped correctly β snapshot β delete, all jobssucceeded). - β
GetMetricsSummary + ListBuckets β capacity/I/O/recovery rollup and RGW bucket inventory
for product consoles (see
docs/PRODUCTS.md). - Follow-ups: generated gRPC client stubs vendored into each product (Veyron, Transiva, β¦).
β DataBridge β cloud-to-edge database mobility (six source engines)β
A migration control plane layered on Atlas (atlas-databridge): migrate managed cloud databases
to open engines at the edge on Ceph-backed storage.
- Pipeline as async jobs + a reconciler: discover β assess β provision β full-load β CDC β validate β cutover β rollback. Admin-guarded cutover (validated + validation passed + CDC lag threshold); rollback window.
- Six source engines: Postgres, MySQL, MariaDB (homogeneous β CNPG/Percona) + Oracle, SQL Server (heterogeneous β Postgres edge, seeded by Debezium's initial snapshot) + MongoDB (homogeneous document β Percona Server for MongoDB, Mongo Kafka sink), all on Ceph-backed storage.
- Real connectors: Postgres/MySQL/MariaDB default; SQL Server (
tiberius) / Oracle (OCI) / MongoDB behind cargo features; precise Kafka CDC lag behind thekafka-lagfeature. - CDC: Debezium on Strimzi/Kafka β JDBC sink to the edge DB (Postgres path).
migrations/0011_databridge.sql;/api/atlas/v1/databridge/*; DataBridge console section;deploy/databridge/operator bundle + Connect image. Gateway image bundles the OCI Instant Client and builds theoraclefeature.- Postgres verified end-to-end on two independent live Rook Ceph clusters (
<ephemeral-ip>,<ephemeral-ip>) including real CDC replication, driven through the deployed gateway. - All six engines' real-connector discovery verified live β MySQL 8.4, MariaDB 11.x, MongoDB 7.0
(replica set), SQL Server 2022, Postgres, Oracle 23ai/26ai Free β through the deployed k3s gateway
(binlog ROW / replica-set change streams /
is_cdc_enabled/ supplemental-log-min βcdc_capable, real db + table/collection names, PKs, counts). - MySQL, MariaDB, MongoDB verified through provision β real full-load (data physically copied sourceβedge) β validate (row/document-count parity) against a lightweight edge.
- MariaDB + MongoDB CDC + cutover verified live (2026-09-01) on the Rook Ceph lab with
Strimzi/Debezium; MySQL (incl.
TIMESTAMPcolumns), SQL Server and Oracle followed on 2026-10-04 (docs/DATABRIDGE.md). - Fake-first: the entire pipeline runs with no cloud/k8s (
make run-databridge), CI-locked bytests/databridge_pipeline.rs. - Follow-ups: TLS for cloud SSL sources; a rollback drill for the heterogeneous engines.
See DATABRIDGE.md.
β Day-2 operations (done, fake-first tested)β
Atlas is operable, not just observe-and-provision. See DAY2.md for the full operator runbook; summary:
- Control-plane durability: job recovery on boot, graceful shutdown, deep
/readyz(DB + driver + worker heartbeats), optional self-state SQLiteβS3 backup. - Job cancellation:
POST /jobs/{id}/cancel(admin) β the single-worker queue's escape hatch for a job wedged inside a hungceph/rbdcall;kill_on_dropon every subprocess spawn actually kills the child, not just the Rust future. Verified live: cancelled a genuinely stuckrbd snap unprotect/rbd.snap_deletejob mid-run on real Ceph. Also verified live: abucket.createquota-set step stuckrunningindefinitely (unboundedradosgw-admincall) blocking the whole queue β cancel freed it immediately; everyradosgw-admincall is now additionally bounded to 12s at the driver layer so this specific case self-resolves instead of needing manual intervention. - Alerting maturity: ack/silence(
?secs=)/resolve lifecycle; rules for jobs-failing, CDC replication error, and tenant-quota-approaching added to the original cluster/pool/OSD set. Verified live against a real open alert (ack β silence honored β resolve). - Cluster ops & maintenance: backend cordon/uncordon,
POST/GET /maintenance {paused}worker pause, OSD out/in/reweight, dynamic backend registration (POST /backends {backend_type:"nfs"|"zfs"}β instantiates a live driver + discovers immediately; verified live that an unreachable server still registers/discovers cleanly since NFS/ZFS are fixture-only). - Governance: token revocation (
POST /auth/tokens/{jti}/revoke), per-minute rate limiting, audit CSV export + retention pruning, chargeback. - Volume lifecycle: orphan-backup GC (
GET /maintenance/orphans), per-image QoS (iops/bps), safe resize-down (allow_shrinkguard). - DataBridge self-heal:
POST /databridge/plans/{id}/cdc/restart+ reconciler auto-restart (bounded, 3Γ) for a stalled CDC stream. - Upgrade pre-flight + rollback:
GET /upgrade/preflightgatesdeploy-remote.sh/deploy-ceph-gateway-remote.sh;--rollbackreverts viakubectl rollout undo. - Cross-cluster DR scaffolding: peers/mirrors catalog, enable/disable/promote/demote (role
guards +
force), preflight checklist, one-click failover runbook (confirm/force), RPO recording. Verified live (control-plane guards + preflight blockers + forced override) against the real lab; the underlyingrbd mirrordata-plane still needs a second Ceph cluster to be production-verified β see DR.md.
β AI Ops Advisor β explainable analysis, incident correlation, MCP toolsβ
Deterministic, read-only (can_execute: false) analysis layered on existing inventory/telemetry β
no model required for the default path. See AI_ADVISOR.md.
- Ops Advisor (
POST /ai/advisor): bounded risk score + level, evidence (capacity, 14-day forecast, recovery counters, open alerts, recent failures), and a prioritized read-only runbook.local/auto/llmmodes β an optional OpenAI-compatible provider (ATLAS_AI_BASE_URL) may rewrite only the executive summary; Atlas always computes and retains the score/evidence/actions. - Incident correlation (
GET /ai/incidents): groups related open alerts + recent job failures into families (data safety, capacity, Ceph recovery, replication, job engine, availability, unclassified) with a bounded confidence score and a likely-cause explanation; optional LLM-narrated root-cause summary (mode, stilllocalby default) on top of the same deterministic grouping. - What-if capacity planning (
POST /ai/what-if): projects risk/utilization/days-to-full under hypothetical added capacity, growth rate, and alert/recovery-resolved assumptions, without mutating inventory. - Explainable anomaly detection (
GET /ai/anomalies): median/MAD-based detector over capacity growth, read/write byte/op deltas, concurrent jobs, and open alerts β resistant to older spikes contaminating the baseline. β Pauses itself rather than scoring on bad telemetry: returnstelemetry_status: "stale"(>15 min old) or"unavailable"(missing/invalid/future-dated timestamp, or no samples at all) withanomalies: []and an explanatory warning instead of presenting a stale spike as a current incident. - MCP tool exposure (
crates/atlas-gateway/src/mcp.rs,mcpfeature): 27 read-only tools (inventory,ops_advisor,list_incidents,detect_anomalies,what_if_capacity,io_risk, DR, jobs, snapshots) plus two opt-in guarded writes (create_snapshot,ack_alert; two-phase confirmation token, audited; off unlessATLAS_MCP_WRITE_ENABLE=1), and an authenticated/mcpon theatlas-ioagent. Failover, delete, restore and disk operations are deliberately not exposed. See MCP.md and API.md.
β Slice 3+ β Enterprise & product integrationβ
- Product integrations: Veyron (VM datastores), Transiva (direct-to-RBD migration; owner id
hyper2kvm), GuestKit (read-only snapshot inspection), PacketWolf (VMβOSD network/storage RCA), Ragnarok (AI storage facts), Aether (intent YAML), Machina (RGW/CephFS artifacts), HyperSDK (install/onboard CLI). - β
Monitor worker (
atlas-monitor): periodic discovery + alert rules (cluster unhealthy, pool near-full 75/85%, OSD down) β realGET /alerts?state=,POST /alerts/evaluate. Deterministic alert ids upsert on recurrence and resolve when conditions clear. Verified on real Ceph (HEALTH_WARNβ one open cluster alert). - β
Ceph mgr Prometheus scrape: each tick scrapes
rook-ceph-mgr:9283/metrics, keeps a curated whitelist (capacity, OSD up/in/latency, pool usage, pg, health) intostorage_metrics, and raises a high-OSD-latency alert (100ms warn / 1000ms critical).GET /metrics/ceph?prefix=. Verified on real Ceph (44 metrics incl. real capacity +osd.0apply latency). - β
Client I/O + recovery metrics/alerts: the scrape also keeps per-pool client I/O counters
(
ceph_pool_rd/wr[_bytes]β rate them for IOPS/throughput) and recovery/backfill health (ceph_pg_recovering/backfilling/*_wait,ceph_num_objects_degraded/misplaced/unfound). A recovery alert warns while PGs recover/backfill and escalates to critical on unfound objects, clearing when done. Verified live: client counters populated; recovery alert stays clear on an idle cluster (no false positive). - β
Rook CRD read integration:
atlas-driver-k8s::rookreads Rook's ownceph.rook.io/v1CRs (CephCluster,CephBlockPool,CephFilesystem,CephObjectStore) as a second, precise source of truth alongside the existingceph/rbdCLI path β additive, nothing CLI-based was removed. Poolkindclassification (previously a pure name heuristic) is now overridden with an exact Rook-CR-derived kind during discovery when a k8s driver is attached (atlas_driver_k8s::rook::known_rook_pool_kinds, wired throughatlas-discovery::run_discovery's newrook_pool_kindsparameter, mirroring the existingRbdOwnersenrichment).GET /ceph/health-rollupcross-checks the CLI-derived severity againstCephCluster.status.ceph.healthand surfaces arook_disagreementwhen they differ, falling back to a coarser Rook-CRD-only rollup if thecephCLI path itself fails but Rook is reachable. New additiveGET /ceph/rook-statusexposes the raw CephCluster/CephBlockPool/CephFilesystem/CephObjectStore CR view.ATLAS_ROOK_NAMESPACE/ATLAS_ROOK_CLUSTER_NAME(both defaultrook-ceph) configure it. Verified live against the real single-node Rook lab (<ephemeral-ip>, redeployed viascripts/deploy-remote.sh):GET /ceph/rook-statusreturned the realCephCluster(phase: Ready, health: HEALTH_WARN) plus the realrbd-nvme-prod/zyvorfs/zyvor-rgwCRs allReady;GET /ceph/health-rollupshowedsources: ["cli","rook-crd"]with norook_disagreement(the CLI and Rook's own health agreed). This pass also caught and fixed a real RBAC gap: theatlas-gatewayServiceAccount's ClusterRole had noceph.rook.iogrant at all, so the very first live call 403'd (cephclusters.ceph.rook.io is forbidden) β the ClusterRole indeploy/k8s/atlas-gateway.yamlnow grantsget/list/watchoncephclustersand full CRUD oncephblockpools/cephfilesystems/cephobjectstores, pluscreate/update/deleteonstorageclasses(previously read-only) for the lifecycle-automation jobs below. - β
Rook lifecycle automation: create/list/delete Ceph pools/filesystems/object stores through
Rook CRs from the API instead of hand-edited YAML manifests, following the same async-job pattern
bucket.createalready uses (CR apply β pollstatus.phase == "Ready"β done).POST/GET /ceph/poolsapplies aCephBlockPool(replicated pools only β erasure coding isn't modeled) + a matchingzyvor.dev-labelled StorageClass;POST/GET /ceph/filesystemsapplies aCephFilesystem+ RWX CephFS StorageClass;POST/GET /ceph/object-storesapplies aCephObjectStore+ RGW bucket StorageClass.DELETE /ceph/{pools,filesystems,object-stores}/ {name}removes the CR + StorageClass, guarded409while a volume/bucket still references it (?force=trueto override) βstorage_bucketsgained astorage_classcolumn (migration 0029) so the object-store guard can find dependent buckets, mirroringstorage_volumes.storage_class_name's existing role for pools/filesystems.atlasctl {ceph-pools,create-ceph-pool,delete-ceph-pool}(and the filesystem/object-store equivalents). CephCluster-level changes (OSD/mon topology) are explicitly out of scope β left as a documented follow-up, not built. Verified live against the real single-node Rook lab (<ephemeral-ip>):POST /ceph/poolscreated a realCephBlockPool(replicated_size: 1, failure_domain: "osd", matching this cluster's actual 1-OSD capacity) that reachedphase: Ready; a real PVC (storageClassName: zyvor-atlas-test-pool-3) bound against the StorageClass the job created (kubectl get pvcβBound);DELETEremoved the StorageClass and the PVC was cleaned up. Two real gaps found and fixed along the way: (1) the pool/filesystem/object-storereplicatedspec hard-codedrequireSafeReplicaSize: <size >= 3>, an invented heuristic that doesn't match any static manifest in this repo β every one ofdeploy/rook-ceph-lab/*.yamlsets itfalseunconditionally (the lab never has enough failure domains for Ceph's "safe" check to pass);crates/atlas-jobs/src/dispatch/rook.rsnow always passesfalse, like the manifests. (2) the gateway's ClusterRole had no RBAC forceph.rook.ioat all (see the read-integration entry above) β fixed in the same pass. Known follow-up, not a bug in this code: a real Ceph cluster defaultsmon_allow_pool_delete=false(confirmed via Rook's own reconcile error,Error EPERM: pool deletion is disabled) βDELETE /ceph/pools/{name}correctly deletes the k8s-level CR + StorageClass, but Rook's own finalizer can't complete the underlying Ceph pool purge until an operator sets that mon config explicitly; until then the CR sitsTerminatingindefinitely (harmless β StorageClass is already gone, so nothing new can provision against it). A future pass could have the delete job set/restore that mon config around the delete the same way this verification did manually, or document it as an explicit day-2 prerequisite. - β
CephFS shared volumes (RWX):
POST /volumes {"policy":"shared"}provisions a ReadWriteMany CephFS volume (zyvor-cephfs-shared) that multiple pods mount at once β for ISO libraries, templates, and multi-writer product data (GuestKit, Machina). Single-node CephFS filesystem + StorageClass manifest atdeploy/rook-ceph-lab/single-node/cephfs-sc.yaml. Verified live on real Ceph: two pods mounted the same volume concurrently and saw each other's writes. Thesharedpolicy now also correctly tags the volumekind: "filesystem"in inventory (previously it silently defaulted tokind: "block"since the request'skindfield is optional and policy resolution didn't correct it) β a mistagged CephFS volume looked exactly like a deleted RBD image to the real driver's RBD-only discovery and got silently pruned from inventory on the very next discovery pass, even though the PVC kept existing and consuming capacity, invisibly to Atlas. Verified live: created asharedvolume, ran a discovery pass, it now survives (previously vanished fromGET /volumesand returned404, requiring manualkubectl delete pvcto clean up the now-orphaned real resource). - Zeus OS UI β Storage Center. This collision is unresolved (see STATUS.md):
atlasis already a Zeus OS module codename ("Machine Finder") inv9s.- A
ZeusStorageCenter.tsx+web/src/routes/storage.rsalready ship β decide whether Atlas's Storage Center absorbs, replaces, or sits beside them.
- Additional drivers behind the same trait: NFS, ZFS, SAN, cloud block, external Ceph import.
- β
Scheduled snapshots (protection schedules):
POST /volumes/{id}/schedule {interval_secs, keep}registers a schedule (snapshot_schedules); a background worker (ATLAS_SNAPSHOT_TICK_SECS) snapshots the volume on cadence and prunes its scheduler-created snapshots tokeep.GET /schedules,DELETE /schedules/{id},atlasctl schedule-snapshots. Verified live: snapshots appear on interval and retention holds steady atkeep(delivers the policy catalog's "hourly snapshots"). - β
Scheduled backups: the same schedule with
kind:"backup"(+bucket_id,mode) runs periodic backups to RGW and prunes them tokeep(delivers the "daily backup" half). Verified live: backups created on cadence, retention never exceedskeep.atlasctl schedule-backups. - β
Multi-tenancy quotas: per-tenant
max_bytes+max_volumes(storage_tenant_quotas, 0 = unlimited).PUT /tenants/{id}/quota(admin) sets them;GETreturns limits + live usage.POST /volumes(REST + gRPCCreateVolume) rejects an over-quota create (409 /resource_exhausted) before enqueueing.atlasctl quota|set-quota. Verified live: byte + count limits both enforced, raising the limit admits the create, usage tracked from live volume rows. - β
Service-account tokens:
POST /auth/tokens(admin) mints a scoped HS256 JWT (sub/role/exp) for a product/service account β the shared secret never leaves Atlas. TTL is clamped to[60s, 90d]and issuance is audited.atlasctl issue-token. Verified: minted operator token creates volumes, viewer is403, no token401, and a non-admin cannot mint (test); live the endpoint returns a valid JWT with the expected claims. - β
OIDC/SSO login:
GET /auth/oidc/{status,login,callback}(optional β auto-disabled unlessATLAS_OIDC_ISSUER_URL/_CLIENT_ID/_REDIRECT_URLare set) is a second way to obtain a console session β a successful OIDC round-trip mints the exact same kind of Atlas JWT/auth/logindoes, so it sits alongside local username/password login, not in place of it. PKCE- CSRF state + nonce; the
groupsID-token claim maps to a role viaATLAS_OIDC_ADMIN_GROUP/_OPERATOR_GROUP(no match βviewer). Verified live end-to-end against a throwaway Dex instance (deploy/dex-lab/): three static test users in different Dex groups each landed with the correct Atlas role (admin/operator/viewer-by-default) after the full browser redirect β login β callback β session round-trip.
- CSRF state + nonce; the
- β
Tenant-scoped reads: found via a production-readiness audit ahead of a prospective
multi-tenant bank deployment β JWTs carried no
tenant_id, soGET /volumes,/volumes.csv,/volumes/{id},/buckets, and/buckets/{id}returned every tenant's data to any authenticatedviewer, not just their own.Claims/Actornow carrytenant_id(set at mint time for local login,POST /auth/tokens, and OIDC login β the latter via a configurableATLAS_OIDC_TENANT_CLAIMID-token claim, defaulting to"global"when unset so existing single-tenant deployments are unaffected);auth::tenant_scope/require_tenantenforce it on those five routes, withadminstaying cross-tenant by design.crates/atlas-gateway/tests/ tenant_isolation.rsis the regression guard. Console users (POST /auth/users) and issued tokens can now be created with an explicittenant_id(default"global"). - β
Secrets-manager integration (Vault + External Secrets Operator):
deploy/vault-lab/β a throwaway Vault (dev mode) + ESO demonstrate the real pattern a bank deployment would use: secrets live in Vault, ESO syncs them into an ordinary K8sSecretvia Vault's Kubernetes auth method, and Atlas's Deployment references thatSecretthrough the exact samesecretKeyRefit already uses foratlas-gateway-authβ no Atlas-side code or config shape changes needed. Verified live end-to-end against a dedicated demo secret (not the liveatlas-gateway-auth, deliberately, to avoid disrupting the running gateways): Vault β K8s-Secret sync confirmed byte-for-byte identical, and a live rotation in Vault propagated to the K8s Secret automatically within the 30s refresh interval, with nokubectl/redeploy on the Atlas side. Adopting this for the realatlas-gateway-authSecret is a two-field YAML change once a real Vault is available (see the README's "Adopting this for real"). - β
JWT secret rotation: previously a single static
ATLAS_JWT_SECRETwith no rotation path β changing it instantly invalidated every outstanding session/service-account token with no grace period.ATLAS_JWT_SECRET_PREVIOUS(optional) is still accepted for validating tokens (never for minting new ones) during a rotation window: set it to the outgoing secret when rotating to a newATLAS_JWT_SECRET, deploy, then remove it once confident no outstanding token still uses it (bounded by each token's own TTL, capped at 90 days).auth::decode_tokenis the shared fallback-decode helper β both the RESTauth_middlewareand the gRPC interceptor use it, so the two edges can't drift on this behavior. - β
Audit-log export before pruning:
ATLAS_AUDIT_EXPORT_URL(optional, alongside the existingATLAS_AUDIT_RETENTION_DAYS) batches rows due for retention pruning into one JSON POST to an external sink β SIEM webhook, Splunk HEC, Elastic/Fluent Bit HTTP input, anything that accepts JSON β and only deletes the rows that were actually exported (atlas_monitor::audit_export::export_and_prune); a failed export leaves rows in place for retry on the next 6h tick rather than silently dropping them, closing the "audit data just disappears with no external record" gap a compliance review would flag first. Straight-lineprune()(no export) remains available whenATLAS_AUDIT_EXPORT_URLis unset. - β
SIEM-export network path verified against real infra:
deploy/siem-lab/stands up a minimal HTTP receiver in the lab (stdlib-only Python, no deps) standing in for a SIEM's ingestion endpoint (Splunk HEC, Elastic/Fluent Bit HTTP input, a generic webhook collector). A new opt-in live test (crates/atlas-gateway/tests/audit_export.rs'slive_export_against_siem_lab_receiver,#[ignore]d β run withcargo test --test audit_export -- --ignored) exercises the realexport_and_pruneHTTP client against it over the actual lab network, confirmed via the receiver's own logs receiving the exact expected payload β proof against an independently-implemented server, not just the existing tests' in-process axum mock agreeing with itself. Deliberately doesn't touch the live gateway'sATLAS_AUDIT_EXPORT_URL/ATLAS_AUDIT_RETENTION_DAYSβ turning those on for real is a standing retention-policy change for the platform team to make deliberately, not a side effect of this. - β
Postgres HA scaffolding verified against real infra:
deploy/postgres-lab/stands up a throwaway Postgres in the lab; a new opt-in live test (crates/atlas-inventory/tests/postgres_live.rs'sconnect_and_migrate_against_real_postgres,#[ignore]d,--features postgres) provesconnect_postgres()/migrate_postgres()actually open a connection and applymigrations-postgres/against a real server β not just a compile check behind the feature flag. Also caught and fixed a real gap:migrations-postgres/had drifted 3 migrations behindmigrations/(missingrbd_snapshots, the native-id unique index, and β critically β this session's ownconsole_users.tenant_idtenant-isolation column), now back in file-for-file parity and re-verified idempotent (migrate-twice is a no-op, matching what a rolling pod restart does). This proves the connection/schema half of Phase-1 HA; the SQLite-only query layer (SqlitePoolused throughoutatlas-inventory) is unchanged and still the explicitly out-of-scope larger effort β seedeploy/postgres-lab/README.mdand the known-limitations note below. - β
Query layer ported off SQLite-only
SqlitePool/?ontosqlx::AnyPool/$Nβ closes the gap the bullet above deliberately left open. Resolvesdocs/HA.md's previously-open "dual query modules vssqlx::Any" architecture question in favor ofsqlx::Any, made viable by first eliminating every genuinely SQLite-specific construct across all ~250sqlx::querycall sites inatlas-inventory/atlas-jobs/atlas-monitor/atlas-gateway/atlas-databridge:?β$Nplaceholders (verified live thatsqlx::Anydoes zero placeholder rewriting itself, so both backends' concrete drivers now receive identical query text);strftime('now', ...)(~80 sites) β a Rust-boundchronotimestamp;INSERT OR IGNOREβON CONFLICT DO NOTHING;COLLATE NOCASEβlower(x) = lower($N)(with a new expression index in both migration directories); and a real portability bug caught along the way β SQLite's 2-argument scalarMAX(a, b)(used in the job engine'smark_running/try_claim/reclaim_stale_running) has no Postgres equivalent (MAX()there is aggregate-only) β rewritten toCASE WHEN a > b THEN a ELSE b END. Theatlas-inventorypostgresCargo feature is gone; Postgres support is unconditional.crates/atlas-inventory/tests/postgres_live.rsnow proves the query layer itself on a real Postgres β not just connect+migrate β exercising the job-claim/reclaim/retry state machine (where theMAX(a,b)bug was actually found) and theCOLLATE NOCASErewrite's case-insensitive username lookup.scripts/check-migrations-parity.sh(new CI gate) preventsmigrations-postgres/drifting behindmigrations/again, the way it silently did twice before. Seedocs/HA.md, which this closes most of β remaining at the time: a dual-backend CI job (closed by the next bullet) and DB-backed rate limiting (found to be blocked on a real constraint β the gRPCtonic::Interceptorclosure is synchronous β not just unstarted work; seedocs/HA.md's detail). - β
Dual-backend CI proves the query layer, not just the connection β new
postgres-testjob in.github/workflows/ci.ymlruns the full ~200-testatlas-gatewayintegration suite a second time against a realpostgres:16service container (plusatlas-inventory'spostgres_live.rstests), via a newcrates/atlas-gateway/tests/common/mod.rsshared helper: every test's database setup goes through onefresh_database_url()call that's a throwaway SQLite temp file by default (unchanged from before) or, whenATLAS_TEST_DATABASE_URLis set, a freshlyCREATE DATABASE'd Postgres database per test β same per-test isolation guarantee, different backend, zero changes to any of the ~200 existing test bodies/assertions. This is exactly the "run the existing suite against both backends" ideadocs/HA.mdhad flagged as still open, and it did its job immediately: running it for the first time surfaced four real, previously undiscovered Postgres schema/query bugs, none of them caught by "does it compile" or by SQLite alone β (1) several byte-capacity columns declared PostgresINTEGER(4 bytes, ~2.1GB max) instead ofBIGINT, since SQLite's ownINTEGERis always 8 bytes regardless of the declared name (fixed in migration0032); (2)SUM(bigint)returns PostgresNUMERIC, whichsqlx::Anycan't decode at all, requiring an explicitCAST(... AS BIGINT)at three call sites; (3) the equivalent gap for floats βREALcolumns bound to Rustf64needed widening toDOUBLE PRECISION(migration0033); (4) SQLite's JSON1 functions (json_extract/json_set) have no Postgres equivalent whose query text is identical on both backends, so the three call sites using them were rewritten to pull raw JSON text and do the read/merge in Rust instead, matching this migration's established date-math pattern. All four are now covered by the live suite going forward. Seedocs/HA.md's "Landed" table for the full detail. - β
Helm chart: Postgres-backed multi-replica deployment β
deploy/helm/atlas/values.yaml's newdatabase.kind: sqlite|postgres(+database.existingSecret/secretKey, never an inlined URL) switchestemplates/deployment.yaml/pvc.yamlbetween the existing single-replica shape (localReadWriteOncePVC,Recreaterollout) and a Postgres-backed one (ATLAS_DATABASE_URLfrom a Secret, no PVC rendered at all,RollingUpdate,replicaCountfree to raise above1). Both render paths verified withhelm lint+helm template/helm install --dry-run. Seedeploy/helm/atlas/README.md's "Multi-replica (Postgres-backed) deployment". - β
Supply-chain audit gate:
cargo deny check(CI job +make audit,deny.toml) β known- vulnerable/yanked advisories, disallowed licenses, unknown registries/git sources. Scoped to default features (what's actually shipped); the optional DataBridge connectors are compile- checked separately and excluded here sincetiberius(SQL Server) currently pulls in an unmaintained/vulnerable rustls 0.21-era chain not worth blocking the default build over. Found and fixed a real memory-exhaustion DoS (RUSTSEC-2026-0195) by bumpingrusty-s30.5β0.10 (zero code changes needed β it also dropped the vulnerablequick-xmldependency entirely). The handful of remaining "unmaintained, no safe upgrade available" advisories (rsa's Marvin Attack timing side-channel; a small family of unmaintained-but-not-vulnerable crates transitive viakube-runtime0.95) are explicitlyignored with justification, re-checked whenever deps update. - β
Console password hashing: switched from plain SHA-256 to Argon2id (
crates/atlas-gateway/ src/auth.rs'shash_password/verify_password_hash) β a leakedconsole_userstable can no longer be brute-forced offline at GPU/ASIC speed. Backward-compatible: existingsha256$...hashes still verify, so no forced password reset on upgrade; every newly hashed/changed password gets the new Argon2id format. - β
No plaintext secrets in committed manifests:
ATLAS_ADMIN_PASSWORDandATLAS_OIDC_CLIENT_SECRETmoved from a literalvalue:indeploy/k8s/atlas-gateway*.yamltosecretKeyRef(scripts/ensure-atlas-auth-secret.shnow also generates a strongadmin-password; the OIDC secret points at the samedex-oidc-clientSecretdeploy/dex-lab/ up.shalready creates, rather than duplicating the value).Config::validate_for_start()now also refuses to boot withATLAS_AUTH_REQUIRED=1and a weak/default admin password, mirroring the existing JWT-secret guard. - β
Audit-log query API:
GET /audit(operator) returns the compliance trail newest-first withactor/action/resource_type/resource_idfilters + boundedlimit. Verified live. - β
Enriched
/metrics/summary: capacity-used %, snapshot/bucket/backup counts, client-I/O rollups (ceph_pool_rd/wr[_bytes]) and recovery status. Verified live. - β
gRPC parity:
ExpandVolume+ListSnapshotsadded to the edge (verified via grpcurl). - β
Volume ownership + labels:
GET /volumes/{id}/bindingslists product ownership;GET/PUT /volumes/{id}/labelsreads/merges user labels into volume metadata. Verified live. - β
GET /tenants: overview of every tenant with volumes/quota (usage + limits). Verified live. - β
Per-tenant policies:
PUT /tenants/{id}/policies/{intent}(admin) remaps an intent to a specific StorageClass + access/volume mode for one tenant;create_volumeapplies it (precedence: request-pinned SC βΊ tenant override βΊ built-in catalog).GET/DELETEtoo;atlasctl set-tenant-policy. Verified live:acme/databaseβ CephFS RWX whileglobex/databasestayed on RBD RWO, and deleting the override revertedacmeto the catalog. - Multi-tenancy: complete (quotas + service accounts + per-tenant policies, PDF Β§14).
- DR: RBD mirroring, secondary-cluster restore, one-click failover runbook (PDF Β§16.3).
- β
Two new pluggable backends: RustFS (S3-compatible object storage) and Longhorn.
atlas-driver-rustfs/atlas-driver-longhornfollow the same fake/real, never-fabricate convention every other driver already has. RustFS discovery (ListBuckets/health) is anonymous and unsigned for now; Longhorn discovery is read-only over its Kubernetes v1beta2 CRDs (nodes/volumes), with PVC provisioning going through the existing Kubernetes StorageClass path rather than this driver. Both are wired through the sameBackendType/capability-catalog/ dynamic-registration machinery NFS/ZFS already use β seedocs/RUSTFS.md,docs/LONGHORN.md. - β
Raw disk provisioning:
POST /zfs/pools/from-deviceruns defense-in-depth safety checks (lsblk/findmnt/wipefs/zpool status, dynamic root/boot-disk detection β never the static-heuristic allow-list alone) beforezpool create(never-f);POST /ceph/devicespatches theCephClusterCR's device list via a scoped merge-patch, cross-checked against Rook's own device-discovery ConfigMap (fails closed if that's unavailable), then polls to completion through the job engine's existing retry/backoff machinery rather than blocking its single worker β Rook's reconciliation can take several minutes. Manual device-path entry only in this slice (no auto-discovered device list). Seedocs/DISKS.md. - β
RustFS is now the primary object backend β
POST /bucketsdefaults to it (a direct, synchronous, signed S3CreateBucket, no Kubernetes operator involved) instead of Ceph RGW;"backend_id": "bkd_ceph_lab"keeps the original RookObjectBucketClaimpath working unchanged.atlas_driver_rgw::S3Target(already generic S3, not RGW-specific despite the name) gainedcreate_bucket/delete_bucket/bucket_exists; credentials resolve viaStorageBackend.connection_refβ a Kubernetes Secret. DataBridge's object-migration leg and Atlas's own self-state backup were already backend-agnostic at the code level (both already built on the same genericS3Target) β repointing either to RustFS is a config/deploy-manifest change, not a code change, and neither has actually been repointed in the lab yet (no real RustFS cluster to point at). Seedocs/RUSTFS.md's known-unverified-risks list (CreateBucketbody, presigned URLs, multipart β none tested against a real RustFS server yet).
β macOS storage (0.7.0)β
- β
atlas-driver-macos: APFS containers, volumes and other partitions viadiskutilplists, read-only by default. - β
Guarded external-disk lifecycle (
/macos/*): erase, add/delete volume, eject; admin, typed confirmation, jobs, audit; internal disks, the running system and system volumes are refused twice (route and job). - β
Fleet:
atlas-mac-agentper Mac plusRemoteMacosDriver(ATLAS_MACOS_AGENTS); launchd installer; release archives. - β
Mac as client: NFS/SMB/
atlasctldocumented (docs/MACOS_CLIENT.md); native FUSE mount on macOS is a documented no-go. - β Disks page UI, container resize, local snapshots, remote writes, SMART/capacity alerts. Real-hardware write commands are unverified; see STATUS.md.
Known limitations (current)β
- DR data plane verified on a lab, not per deployment: the real
rbd mirrorpaths, including two-way mirroring and clean failback, were drilled between two Rook clusters on 2026-10-04 and 2026-10-06.dataplane_verifiedstays a per-deployment flag (ATLAS_DR_DATAPLANE_VERIFIED) that an operator sets after drilling their own sites. Journal-mode and pool-mode mirroring followed on 2026-10-06. Consistency groups (/volume-groups) snapshot and roll back locally only: no released Ceph mirrorsrbd groups across sites. See DR.md. - RustFS removed: Atlas's first-party RustFS integration (driver, admin proxy, Helm chart,
console pages, drive/instance install jobs) was later discarded in favor of Ceph RGW as the
default object backend, with any S3-compatible endpoint usable as a bring-your-own backend via
atlas-driver-rgw. Seedocs/RUSTFS.mdandCHANGELOG.md. - Raw disk provisioning unverified against real hardware/a real Rook cluster: the ZFS
device-safety checks and the Ceph/Rook device-claim path (
docs/DISKS.md) are fake-mode and unit-tested only. The Ceph path's entire safety design further depends on the target Rook install running its device-discovery DaemonSet (ROOK_ENABLE_DISCOVERY_DAEMON) β unconfirmed for the lab's Rook install as of this writing. - arm64 container images: not shipped, tested and rejected for now. A real timing probe
(
.github/workflows/ci.yml'sdockerjob, temporarily, on a throwaway branch/PR) built the fullDockerfile(heaviest image β all DataBridge features: librdkafka/cmake, ODPI-C, tiberius) forlinux/arm64via QEMU emulation on the standardubuntu-latestrunner: it was cancelled after 70+ minutes still running, vs. ~10.5 minutes for a native amd64 build of the lighterDockerfile.cephin the same run β 6.7x+ and still climbing, not a one-off fluke. QEMU full-emulation is the wrong approach for this workspace's dependency weight; a real fix would need native cross-compilation (a properaarch64-unknown-linux-gnutoolchain + cross-linked C deps) rather thanplatforms: linux/amd64,linux/arm64on the existingdocker/build-push-actionstep, which is a materially larger effort than the one-line change this looked like at first. - Bank-grade hardening gaps (found via a production-readiness audit; tenant-scoped reads,
plaintext-secret-in-manifest, weak password hashing, missing supply-chain scanning,
destructive-audit-pruning-with-no-export, and rate-limiting/self-state-backup-off-by-default
from that audit are already fixed above β both
deploy/k8s/atlas-gateway*.yamlnow shipATLAS_RATE_LIMIT_RPM=600and a workingATLAS_STATE_BACKUP_*block against a dedicated RGW user/bucket): the default deployment is still single-replica SQLite with aRecreaterollout. That is a deployment choice, not a missing query layer. Postgres, Helmdatabase.kind, and DB-backed cross-replica rate limiting are implemented and lab-verified (see the checkmarks below anddocs/HA.md). What remains before production support is deploying a real HA Postgres and an enterprise IdP (OIDC/SSO is only verified against a throwaway Dex instance). The audit-log SIEM export and secrets-manager integration patterns are now both verified against real (lab) infra (deploy/siem-lab/,deploy/vault-lab/β see below); a real deployment still needs the bank's actual SIEM/Vault swapped in for the lab ones. None of these block a non-production pilot; all are gates before a production go-live. - β
Non-Postgres DataBridge streaming CDC + cutover: all six source engines are verified live
through cutover (
docs/DATABRIDGE.md). MySQLTIMESTAMPcolumns work since the edges moved to the Debezium JDBC sink (2026-10-03); MySQL, SQL Server and Oracle cutover were driven in-cluster on the Rook Ceph lab on 2026-10-04. Heterogeneous (SQL Server / Oracle) validate stays advisory. - β
NFS/ZFS drivers now have a real mode, not just fixtures:
ATLAS_NFS_DRIVER_MODE/ATLAS_ZFS_DRIVER_MODE(fake, the default β unchanged demo/test behavior β orreal; Helm chart:nfs.driverMode/zfs.driverMode) selectRealNfsDriver/RealZfsDriver(atlas-driver-nfs/atlas-driver-zfs), mirroringCephDriverMode's existing Fake/Real split. Real NFS runsshowmount -e <server>(an RPC, no mount performed) to prove the server is actually reachable and get its real export list β a configured export the server doesn't have is dropped, never fabricated; per-export capacity comes fromdfon the export's mount point when it happens to already be locally mounted (checked via/proc/mounts),Noneotherwise (NFS has no standard remote capacity-without-mounting RPC β reporting "unknown" beats reporting a wrong number). Real ZFS runszpool list -Hp/zfs list -Hplocally, on whatever host the gateway process itself runs on β ZFS has no remote query protocol the way Ceph'sceph/rbdCLI does, so a genuinely remote ZFS host (SSH, a vendor API) isn't supported yet; a configured zpool that isn't actually locally importable is dropped, never fabricated. Both real drivers propagate a real error (never a silent fake-data fallback) when the target is unreachable β proven by new tests asserting exactly that. Both gateway images need the respective client tools onPATH(nfs-common/showmountfor NFS,zfsutils-linux/zpool+zfsfor ZFS) β neither is bundled by default; the Fake drivers remain the zero-dependency default for demos/tests/CI. - β
Cross-replica rate limiting β resolves the gRPC-interceptor-is-synchronous blocker that had
this scoped as its own follow-up, not a mechanical fix.
RateLimiter::allow()(crates/atlas-gateway/src/state.rs) stays exactly as synchronous and database-free as before β still safe to call from the gRPC path's synchronoustonic::Interceptor, no risk of blocking the Tokio runtime β because the DB awareness moved entirely into a separate periodic background task,spawn_rate_limit_sync(ATLAS_RATE_LIMIT_SYNC_SECS, default2s), the only thing that ever touches the newrate_limit_counterstable (migration0034,atlas_inventory::rate_limit). Each replica writes its own current-window count and reads back every actor's cluster-wide total; an actor already over budget cluster-wide is flagged and denied on the next localallow()call immediately, even if that replica's own local count hasn't hitrpmyet β eventually consistent within one sync interval, the standard, documented tradeoff for a governance/abuse-prevention control rather than a hard security boundary.window_minute/countwere deliberately kept plainINTEGER(notBIGINT, unlike this session's earlier capacity-column fixes) since both are guaranteed small, which also sidesteps theSUM(bigint)βNUMERIC-decode issue entirely β live-verified against real Postgres, not just reasoned about. The actualallow()/cluster-flag interaction is covered by fast, deterministic unit tests (no timing-based multi-process simulation needed); harmless on a single SQLite replica (the sync task runs, finds only its own counts, confirms what the local check already knew). Helm'srateLimitRpmpicks this up automatically oncedatabase.kind: postgresβ no new chart value. - Pool
kindis name-heuristic when no k8s driver/Rook CRs are available (non-Rook Ceph). On a Rook-managed cluster it's now overridden with an exact classification read from liveCephBlockPool/CephFilesystem/CephObjectStoreCRs (see the Rook CRD read integration entry above) β implemented and unit-tested, pending a live-cluster verification pass. - Single-node Ceph reports
HEALTH_WARN(expected: 1 OSD < default size 3). - Per-product integrations beyond the gRPC surface (Veyron VM datastores, Transiva direct-to-RBD migration, GuestKit, etc.) are not yet built β see the per-product rows above.
scripts/test-connectors.sh(container-backed DataBridge connector tests) needs podman/docker; thekafka-lagfeature needscmaketo buildrdkafkaβ both are opt-in and skipped where that tooling isn't present.