Self-hosted S3-compatible object storage in Rust, with undo built in.
One static binary, one data directory, no external services.
Quick start · Features · Maturity · S3 compatibility · Operations · MCP
Warning
Beta (v0.1.0-beta.2). Single node, tested but young. The on-disk format is frozen: data
written by this release opens in every later one. Keep an independent backup.
Formerly named Cairn.
Most self-hosted object stores lose a deleted object for good unless you turned versioning on beforehand. Tidemark keeps history for every bucket, always:
- Undo mistakes. Deleted or overwritten objects, and whole buckets, go to a trash you restore from (30 days by default).
- Time travel. Read or restore any bucket as it was at any instant in that window. No snapshot needed.
- Store once. Identical content is written to disk once; copies and restores cost no space.
- Built-in MCP. AI agents use the same storage under the same IAM rules as everyone else.
- Small. 14.6 MiB static binary, about 10 MiB idle memory on Linux (measured in CI).
git clone https://github.com/solutionforest/tidemark && cd tidemark
scripts/start.sh --uiThis downloads the release binary (checksum verified), creates ./tidemark-data with random root
keys, and serves S3 on 127.0.0.1:9000 and the admin UI on 127.0.0.1:9080. Ctrl-C stops it.
source tidemark-data/credentials.env
aws s3 mb s3://photos
aws s3 cp ./cat.jpg s3://photos/
aws s3 rm s3://photos/cat.jpg # oops
./tidemark admin trash restore --bucket photos --key cat.jpg # back, byte-identicalConnect any S3 app (rclone, restic, Cyberduck, Nextcloud, Immich, Velero…): endpoint
http://127.0.0.1:9000, region us-east-1, path-style, keys from credentials.env.
Docker Compose
printf 'TIDEMARK_ROOT_ACCESS_KEY=%s\nTIDEMARK_ROOT_SECRET=%s\n' \
"$(openssl rand -hex 10)" "$(openssl rand -base64 30)" > .env && chmod 600 .env
docker compose up -d # S3 on 127.0.0.1:9000
docker compose --profile ui up -d # plus admin UI on 127.0.0.1:9080Image ghcr.io/solutionforest/tidemark (amd64, arm64) runs read-only as non-root; data lives in
the tidemark-data volume.
Plain Linux binary
v=v0.1.0-beta.2; t=$(uname -m)-unknown-linux-musl
base=https://github.com/solutionforest/tidemark/releases/download/$v
curl -fLO "$base/tidemark-$v-$t.tar.gz" -fLO "$base/SHA256SUMS"
grep " tidemark-$v-$t.tar.gz\$" SHA256SUMS | sha256sum -c - && tar xzf "tidemark-$v-$t.tar.gz"
./tidemark-$v-$t/tidemark init --data-dir ./tidemark-data
./tidemark-$v-$t/tidemark serve --config ./tidemark-data/tidemark.tomlStart at boot with deploy/tidemark.service. Put TLS and a firewall in
front before exposing it to a network.
Admin UI by hand
One HTML page plus a one-file Python or PHP backend that keeps your keys out of the browser. Local machine only.
export TIDEMARK_ENDPOINT=http://127.0.0.1:9000 TIDEMARK_ACCESS_KEY=... TIDEMARK_SECRET_KEY=...
python3 admin-ui/server.py # or: php -S 127.0.0.1:9080 admin-ui/server.php| S3 API | Buckets, objects, versioning, multipart, CopyObject, ListObjects v1/v2/versions, tagging, Range and conditional requests, 5 checksum algorithms, presigned URLs, lifecycle expiration. Operation list |
| Recovery | Trash for objects, versions and buckets; point-in-time reads (x-tidemark-as-of) and bucket restore. Guide |
| Access control | IAM users, keys and policies, bucket policies, PublicAccessBlock, SigV4. Actions |
| Encryption | SSE-S3 (AES256) and bucket default encryption, master key sealed at rest. Config |
| Malware scanning | Uploads are streamed to ClamAV or your own HTTP endpoint before they are stored; infected files land in a quarantine bucket. Guide |
| AI agents | MCP over stdio or HTTP; every tool call is an IAM-checked request. Guide |
| Operations | Backups of the running server, restore, fsck and scrub, disk-full handling, Prometheus metrics with tested alert rules and a Grafana dashboard (deploy/), health endpoints. Guide |
flowchart LR
apps["S3 apps"] -->|S3 API| api
agents["AI agents"] -->|MCP| api
ui["Admin UI / CLI"] -->|admin API| api
subgraph tidemark["tidemark (one binary)"]
api["IAM check"] --> engine["Engine"]
engine --> meta[("metadata + history")]
engine --> blobs[("blobs, stored once")]
end
| Area | State | Evidence |
|---|---|---|
| S3 API, single node | 🟡 Alpha | 324 of 370 ceph s3-tests (88%) gate every push; aws-cli, rclone, mc and aws-sdk-s3 tested |
| Trash, time travel, dedupe | 🟡 Alpha | Integration tests; restores checked byte-identical |
| Upgrades | 🟢 Beta | On-disk format frozen at beta.1; data from 0.1.0-alpha.3 on opens in every later release, checked in CI on every push |
| Security | 🟡 Alpha | Self-review of the auth code (5 fixes, each with a test), nightly fuzzing, cargo audit and cargo deny in CI; no external audit yet |
| Crash safety | 🟡 Alpha | kill -9 mid-write tests, weekly 2 h soak with a kill every 10 min and a disk-full window, fsck clean after; 13.5 h soak with 81 crashes clean, 24 h run open |
| Performance | 🟡 Alpha | Measured on 4 vCPU cloud disk: ~2 100 small PUT/s, ~5 000 small GET/s, 200–1 100 MiB/s large objects |
| Backups and monitoring | 🧪 New | Live-server backups, 11 alert rules with unit tests and a Grafana dashboard, all run against a real server and Prometheus in CI; new in beta.1 |
| Malware scanning | 🧪 New | ClamAV + EICAR tested end to end; new in beta.1 |
| Clustering, erasure coding, replication, OIDC, Object Lock | ⛔ Not in v0.1 | Single node by design |
Road to stable: the data format is frozen and every upgrade is tested. v0.1.0 needs a
clean 24 h soak and comes after two betas at least two weeks apart with no
data-affecting bug open. Full checklist: PRODUCTION-READINESS.md.
| RustFS | VaultS3 | Garage | Tidemark | |
|---|---|---|---|---|
| Undo an unplanned delete | no | only if versioning was on | no | yes, every bucket |
| Read a bucket at time T | no | prior snapshot only | no | any T in the window |
| Identical bytes stored once | no | no | no | yes |
| MCP with the same IAM rules | separate server | no | no | built in |
| Clustering / erasure coding | yes | yes | replication | no |
| Built-in file browser | yes | yes | no | no (use any S3 app) |
Need several nodes, a file browser or OIDC today? Use VaultS3, RustFS or Garage.
Nothing locks you in: it is plain S3. rclone sync or aws s3 sync to any other S3 store while
Tidemark keeps serving reads. Every release stays AGPL, forkable and buildable.
| Operations | Config | S3 compatibility |
| MCP | Extensions | Design |
| Readiness | Changelog | Security |
Issues only, no pull requests. Open an issue; our AI agent reproduces and fixes it, a maintainer reviews. Details in CONTRIBUTING.md. Security reports go private: SECURITY.md.
No telemetry, no account, no licence key, no CLA.
AGPL-3.0-only. Self-hosting, even commercially, has no obligations beyond the notice. If you modify Tidemark and offer it to others over a network, you must share your changes.

