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 must exist. slivingdoc does not create or configure it.
Permissions
The server needs these permissions, and no more:
| Scope | Actions |
|---|---|
On the objects (arn:...:bucket/*) |
s3:GetObject, s3:PutObject, s3:DeleteObject, s3:CreateMultipartUpload, s3:UploadPart, s3:CompleteMultipartUpload, s3:AbortMultipartUpload, s3:ListMultipartUploadParts |
On the bucket (arn:...:bucket) |
s3:ListBucket, s3:ListBucketMultipartUploads |
Object get, put, delete, multipart upload, and multipart abort are what normal operation needs below the configured prefix. The bucket listing is what the cleanup pass needs, and it can be restricted to that same prefix.
The reusable Terraform module in the tool repository grants exactly this policy. 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 upload and abort, and the bucket listing. The user cannot manage the bucket, IAM, or any other AWS resource. See Set up a bucket for how to apply it.
The compatibility probe
Before it serves any call, the server runs a disposable compatibility probe below the configured prefix. 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, so an expired short-lived login surfaces
as a redacted startup refusal rather than a mid-operation error.
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 a trailing slash,
and preserves a non-root path, and that normalised value becomes part of
the private-state storage identity.
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 testcontainer (chrislusf/seaweedfs:4.42), 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.