S3 requirements
The exact object and bucket permissions, the startup compatibility probe and its three conditional-write guarantees, custom endpoints and path style, and what is not required.
The bucket comes from you, or from slivingdoc space new <name>, which
creates one. Either way slivingdoc configures nothing else about it.
This page is about S3 mode. With SLIVINGDOC_TOKEN or a stored login,
slivingdoc uses the hosted service instead and none of it applies. The
default --storage auto picks between the two, and --storage s3 forces
S3 regardless of a token or a login. When a stored login exists, S3
signals matter: a fixed list of AWS variables, --region, --path-style,
and the ~/.aws files decide whether the login or S3 is meant. The list is
under Choosing the storage. See
Choosing the storage.
Permissions
The server’s storage calls need five permissions:
| Scope | Actions |
|---|---|
On the objects (arn:...:bucket/*) |
s3:GetObject, s3:PutObject, s3:DeleteObject, s3:AbortMultipartUpload |
On the bucket (arn:...:bucket) |
s3:ListBucket |
IAM has no separate action for creating a multipart upload, uploading a
part, or completing it: s3:PutObject authorizes all three.
Object get, put, delete, and multipart abort are what normal operation
needs below the configured prefix, and s3:PutObject covers the multipart
upload itself. The bucket listing is
what the cleanup pass needs, and it can be restricted to that same
prefix.
The server never calls the part listing or the multipart-upload listing,
so s3:ListMultipartUploadParts and s3:ListBucketMultipartUploads are
not needed. The reusable Terraform module in the tool repository grants
them anyway, and puts the object actions on the bucket ARN as well as on
bucket/*. It provisions the private bucket together with a
least-privilege IAM user whose inline policy is scoped to that one bucket:
object get, put and delete, multipart abort and part listing, and the
bucket and multipart-upload listings. The user cannot manage the bucket, IAM, or any other AWS resource. See
Set up a bucket for how to apply it.
slivingdoc space is administration, not storage, and it needs two more
permissions on the identity it runs as:
| Scope | Actions |
|---|---|
| Account level | s3:ListAllMyBuckets, s3:CreateBucket |
A bucket listing is what slivingdoc space shows, and a create is what
slivingdoc space new calls. serve, pull, and commit make neither
call, so an identity that only serves a notebook can leave both actions
out; space and space new then fail with the S3 refusal.
The compatibility probe
Before it serves any call, the server proves that the store is compatible by running a disposable probe below the configured prefix, unless a recent result can stand in for it (see below). The probe proves that the store enforces the three guarantees the publication protocol depends on:
| Guarantee | Proved by |
|---|---|
| Conditional creation | If-None-Match: * on a new object |
| Conditional replacement | If-Match: <etag> on an existing object |
| Read-after-write | Reading back what was just written |
It runs against a unique probe/<uuidv7> key below the prefix, and the
sequence is exact: it conditionally creates known bytes, proves that a
second create fails, reads the bytes and the ETag, proves that a wrong
ETag fails without mutating anything, replaces with the correct ETag, and
reads the replacement immediately. It deletes the probe key on success
and after any recoverable failure.
A store that fails the probe is refused at startup with the
INCOMPATIBLE_STORE category. When the failure is an operational error
rather than a missing capability, the diagnostic names the underlying
reason — the S3 AccessDenied or InvalidAccessKeyId error, for example
— while the probe key and any secret stay redacted. pull and commit
run this same startup sequence.
The probe is eight dependent requests (two creates, three reads, two
replaces, and the delete of the probe key), which costs seconds against a
distant bucket on every one-shot command. So slivingdoc records a passed
probe in probe-ok.json in the notebook’s shared pack-cache directory
(see Configuration),
and reuses that record when the same slivingdoc version starts against the
same endpoint, region, bucket, prefix, and addressing mode within 24
hours. An absent, corrupt, foreign, or expired record just means the probe
runs again. Two consequences:
- The record does not bind credentials. A credential that stopped working, such as an expired short-lived session, is not refused at startup: the first request fails instead.
- The endpoint’s conditional-write behaviour is taken on trust for up to
24 hours. Delete
probe-ok.jsonto force a fresh probe on the next start. With no shared cache directory there is no record, and the probe runs every time.
Note:
HTTP 412from a conditional write is the ordinary, expected precondition failure: another writer won the race. A timeout or connection error after the request bytes were sent is an ambiguous result, not a precondition failure, and is resolved by rereadingcurrent. See Storage model.
Custom endpoints and path style
A custom S3-compatible service is configured with an absolute http or
https --endpoint (environment AWS_ENDPOINT_URL_S3). The URL carries
no user information, query, or fragment — a URL with user information is
refused outright, so a secret cannot echo into a diagnostic.
Configuration lowercases the scheme and host, removes every trailing
slash, and preserves a non-root path, and that normalised value becomes
part of the private-state storage identity. An empty query or fragment
(? or # with nothing after it) is refused too, as is a value whose
normalised form does not parse again.
When --endpoint is empty, the AWS SDK still honours AWS_ENDPOINT_URL
and a profile’s endpoint_url. Those are not part of the storage identity
and do not force path-style addressing, so set --endpoint or
AWS_ENDPOINT_URL_S3 when you mean a custom endpoint.
The server always uses path-style addressing for a custom endpoint.
--path-style (environment SLIVINGDOC_PATH_STYLE, default false)
extends path-style addressing to the default AWS endpoint as well.
Credentials
slivingdoc has no authentication layer of its own. Credentials come from the AWS SDK default credential chain, resolved at startup:
- Environment variables —
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, plusAWS_SESSION_TOKEN. - The shared config and credentials files (
~/.aws/credentials,~/.aws/config), honouringAWS_PROFILE. - Ambient identity — SSO sessions, ECS and EKS task roles, and the EC2 instance metadata service.
--bucket, --prefix, --region, and --endpoint shape where the
client points, never who it is: no flag carries a credential.
serve resolves the chain once and holds the session, while each pull
or commit invocation resolves it fresh — so with short-lived STS or SSO
credentials, every invocation needs a currently valid session.
Connect an MCP host covers the three delivery
routes.
Known-compatible stores
| Store | Standing |
|---|---|
| AWS S3 | The reference store. The Terraform module provisions a bucket and its least-privilege user. |
| SeaweedFS | Proven continuously: the tool’s own integration suites run against a pinned SeaweedFS container (chrislusf/seaweedfs:4.42), started through the Docker Engine API, and the repository ships a compose file for local evaluation. |
Any other S3-compatible store is a question the probe answers at startup. Set up a bucket walks through AWS, a hosted alternative, and the local SeaweedFS container.
Warning: The tool’s automated test suites never touch AWS. They use their own S3 test containers, so “it passes CI” is a statement about SeaweedFS, and your own store still has to pass the probe.
What is not required
- Bucket versioning. Not required, and not a prerequisite of the publication protocol.
- Replication, object lock, lifecycle rules, and external backups. These are deployment recovery policies. They complement slivingdoc; choose them for your own recovery requirements.
- A bare repository or a
.gitdirectory in the bucket. There is none, and the server never uses a bucket listing to decide which state is current.
If you do enable versioning, note that deleted object versions can be retained, and your lifecycle rules decide when those noncurrent versions stop consuming storage.
Next
- Set up a bucket — AWS, a hosted store, or a local container.
- Storage model — what those permissions are actually used for.
- Configuration —
--bucket,--prefix,--region,--endpoint,--path-style.