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.json to 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 412 from 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 rereading current. 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:

  1. Environment variables — AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, plus AWS_SESSION_TOKEN.
  2. The shared config and credentials files (~/.aws/credentials, ~/.aws/config), honouring AWS_PROFILE.
  3. 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 .git directory 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

Last updated October 21, 2026

Type to search the documentation.