Storage

Connect external storage and run a built-in S3 store with buckets, keys, quotas and lifecycle rules.

In preview storage version 0.1.0 Part of Storage and backups

Actions

27 actions, callable from the panel, the command palette and the API as POST /api/v1/a/<id>. Internal actions used between modules are not listed.

ActionWhat it doesRiskPreview
storage.connection.create Add storage connection medium No dry run
storage.connection.update Edit storage connection medium No dry run
storage.connection.test Test storage connection (read) low No dry run
storage.connection.list List storage connections (read) low No dry run
storage.connection.get Get a storage connection (read) low No dry run
storage.connection.browse Browse a storage connection (read) low No dry run
storage.connection.delete Delete storage connection medium
storage.connection.attach Record or release a user of a connection low No dry run
storage.s3.enable Enable the built-in S3 store high
storage.s3.disable Disable the built-in S3 store critical
storage.s3.status Built-in S3 store status (read) low No dry run
storage.bucket.create Create a bucket low No dry run
storage.bucket.update Update bucket settings medium No dry run
storage.bucket.delete Delete a bucket high
storage.bucket.list List buckets (read) low No dry run
storage.bucket.get Get a bucket (read) low No dry run
storage.object.list List objects (read) low No dry run
storage.object.delete Delete an object medium No dry run
storage.object.presign Presign an object URL (read) low No dry run
storage.lifecycle.set Set lifecycle rules medium No dry run
storage.lifecycle.preview Preview a lifecycle rule (read) low No dry run
storage.key.create Create an access key medium No dry run
storage.key.list List access keys (read) low No dry run
storage.key.rotate Rotate an access key medium No dry run
storage.key.revoke Revoke an access key medium No dry run
storage.usage.get Storage usage (read) low No dry run
storage.reconcile Re-apply storage to its servers low No dry run

Permissions and limits

Permissions

  • storage.connection.view View storage connections
  • storage.connection.manage Create, edit, test and delete storage connections
  • storage.bucket.view View buckets, keys and usage
  • storage.bucket.manage Create and change buckets and lifecycle rules
  • storage.object.manage Browse, delete and presign objects
  • storage.key.manage Create, rotate and revoke access keys
  • storage.s3.admin Enable, disable and reconcile the built-in S3 store

Plan limits

  • storage.connections Storage connections
  • storage.buckets Buckets
  • storage.s3_bytes Object storage
  • storage.s3_keys Access keys

Engineering notes

Generated from modules/storage/docs.md at build d90e9e2. These are the notes the engineers keep next to the code: precise, technical, and honest about what is not done yet.

Model

  • Connections (storage.connection.*): kinds s3, backblaze_b2, wasabi, r2, spaces, azure_blob, gcs, sftp, ftp, ftps, webdav, local_path (admin only, under /srv or /mnt). The agent writes one root-only rclone config per connection (/etc/rc-storage/rclone/<id>.conf, remote name rc) on the account's server; wasabi/r2/spaces derive the endpoint from region / account id. Secrets live in the vault (conn/<id>); the row keeps only a masked hint (••••last4), API responses never carry a secret. test = list + write/read/delete probe (30 s cap) classified as ok / auth_failed / endpoint_unreachable / tls_error / permission_denied / quota_exceeded. Status ok -> degraded -> failing with storage.connection.failed/recovered events; storage.connections.recheck retests the 20 oldest every 15 min. storage.connection.attach lets other modules record a dependency (backups does); delete is refused with the dependents listed.
  • Built-in S3 (storage.s3.enable, admin): the agent downloads the pinned SeaweedFS release (v4.48, SHA-256 pinned in internal/agent/storage.go, refuses on mismatch), creates user rc-seaweed, writes rc-seaweedfs.service (master/volume/filer on loopback, S3 on bind:port) and one static rc-admin identity (keys only in /etc/rc-seaweedfs/settings.json, 0600 root). Customer keys are SeaweedFS dynamic identities (weed shell s3.configure): create/rotate/revoke need no restart and take effect in about a second (the static -s3.config file is NOT hot reloaded; that is why only the admin lives there).
  • Buckets: names 3-63 DNS style, unique server-wide; private or public-read (public = the anonymous identity gets Read:<bucket>); versioning; object lock (COMPLIANCE, default retention) only at creation; CORS; lifecycle (expiry by prefix/age, abort multipart); per-bucket quota; presigned GET/PUT (SigV4 query signature built by the agent with the admin identity, 1 min - 7 days).
  • Keys: scope = all buckets of the account or one bucket (+ optional prefix), read or readwrite, optional expiry. SeaweedFS has no separate delete action: Write covers delete. The secret is returned once (vault keeps it only to rebuild identities). syncAccount rebuilds every identity of the account from the DB (idempotent), also on bucket create/delete.
  • Quotas: storage.quota.enforce (every minute, system actor) measures buckets (fs.du for bytes, S3 listing for object count), compares with the bucket quota and the account limit storage.s3_bytes, flips write_blocked and re-syncs identities (Write/Tagging dropped for blocked buckets, reads stay). Events at 80/95 % (storage.quota.warning) and 100 % (storage.quota.exceeded). So enforcement lags by up to a minute (a single big write can overshoot); SeaweedFS' own s3.bucket.quota.enforce did not act in the lab, so this module does it. Backup-purpose buckets/keys are not counted against the user's bucket/key/byte limits.
  • accounts.deleted subscription removes keys, buckets (object-lock buckets are kept and marked retained) and connections.

Verified in the lab (2026-10-09, FAKENET=full, Ubuntu 24.04, SeaweedFS 4.48, rclone 1.60.1)

See docs/tasks/w4/w4-01-storage-backups.handover.md for the command log: unmodified AWS SDK (aws-cli v2) from another container

  • put/get/list/head/copy/delete, 120 MB multipart (2.8 s, checksum equal), presigned GET/PUT (panel- and SDK-generated), expiry (403 "Request has expired"), tampered key (SignatureDoesNotMatch), anonymous GET only on the public bucket, prefix-scoped read-only key (outside prefix and writes refused), revoke and rotate (old key dead in seconds), quota block + unblock, key expiry, lifecycle and CORS round trips, bucket limit, account deletion cascade, non-empty delete refused.

Known gaps / next

OAuth kinds (Drive, Dropbox, OneDrive), FUSE mounts, per-package allowed kinds (storage.policy.set), object version restore UI, virtual-host-style addressing and custom domains/TLS (need wildcard DNS + ssl; SeaweedFS -s3.domainName), multi-node placement and replication, SSE, usage history/egress metering, S3 request logs. Object count is capped at 50 000 listed objects per scan. Vault has no delete: secrets of removed connections/keys are overwritten with an empty value.