Definition
Bucket vs Workspace
Section titled “Bucket vs Workspace”A bucket is raw S3 — it exists the moment it’s created, whether or not Atlas knows about it. A Workspace is the governance counterpart: a bucket an admin has explicitly registered, which is what unlocks declaring documents, links, search, and role-based access on that bucket.
Registered by an admin
Declare, search, roles
Never registered
Superuser-gated
There’s no separate table joined by an id — the bucket’s name is the join key everywhere, because a bucket can’t be renamed in S3 either, so the name is stable for the row’s whole lifetime. Raw object browsing (list, download, tags) stays open to any authenticated session on any bucket, registered or not — only the governance routes require a Workspace to exist first.
It’s a real bucket, not a shared bucket with a workspace/xyz/ prefix per
team: versioning, Object Lock, lifecycle rules, and encryption are all
configured per bucket in S3, not per prefix, and a real bucket gives
every workspace a hard blast radius — deleting one workspace’s data can
never leak into another’s storage or IAM scope.
Register a workspace from the admin UI or the API, either creating a new bucket or adopting an existing one. Granting roles on it is covered in Authorization.
Object, MetaObject & Document
Section titled “Object, MetaObject & Document”The same file is visible through three different lenses, deliberately never merged into one table:
Read live, every request
Cache of every version
Read live, every request
Status, properties, links
A Link hangs off a Document the same way — source/target,
pointing at another Document, never at an Object directly. A Document also
keeps two drift flags compared live against its Object: orphan
and stale, covered below.
- Object — S3’s own reality. Listing a workspace always reads the bucket live; nothing about “which files exist” is ever cached.
- MetaObject — one row per version of an object, mirrored into Postgres so bucket-wide stats (total size, object count) don’t require a live walk of the whole bucket on every request. Filled in lazily (any request that already fetched an object’s metadata for its own response updates it) and in bulk by the reconciliation scan.
- Document — the governance row. Created only by declaring, idempotent
per file, so retrying a declaration is always safe. Carries a state
(
NORMAL/PROCESSING/SUCCESS/ERROR) and free-form business properties. - Link — a hint that two Documents relate to each other (an invoice and its purchase order, say), framed relative to whichever document the caller is looking at.
No foreign key connects a MetaObject to a Document: a version can exist
with no Document ever declared over it — most files, in practice — and a
Document can be declared before any version has been scanned. Keeping them
independent means either can be populated on its own schedule instead of
one waiting on the other.
Orphan & Stale
Section titled “Orphan & Stale”Two drift flags live on a Document, compared against the live object:
orphan (the object is gone) and stale (the object changed since
declaration).
orphan: false
stale: false
orphan and/or stale → true
Never flips back on its own
orphan/stale cost a live S3 call to check, so nothing forces that check
on every read. They flip to true the moment any request that already
touched S3 for that key notices a mismatch — browsing, opening a detail
view, searching — plus a bulk sweep triggered manually (Sync, on a
workspace or a bucket). They deliberately never flip back to false on
their own: only a person explicitly accepting the current content as the
new baseline clears them. Auto-clearing the moment content happens to match
again would quietly erase real drift history from whoever’s meant to review
it — the flag is a signal for a human, not a cache to keep fresh.
The audit trail follows the same instinct one level up: an entry records
bucket/key directly rather than pointing at a Document row, because a
bare S3 rename or delete can target a key that was never declared at all —
the audit trail can’t assume governance data exists just because an action
happened.