paladin
A multi-tenant control plane for S3-compatible object storage. Applications authenticate with a Paladin credential scoped to a tenant instead of holding storage credentials; Paladin checks each request against the tenant's Cedar policies, resolves the backend and bucket the tenant is on, meters usage against quotas, and writes an audit trail.
Paladin does not proxy traffic to S3. Object bytes move between the client and the storage backend over presigned URLs that Paladin signs; the API carries metadata only. Paladin itself calls S3 to manage buckets and objects — create and delete buckets, open and close multipart uploads, verify a completed upload, copy and delete objects server-side — never to relay a client's upload or download. ARCHITECTURE.md lists every call.
flowchart LR
op(["Operator · console"])
agent(["AI agent · MCP"])
app(["Application · SDK"])
cp["<b>Paladin</b><br/>IAM · Cedar policy · quotas<br/>object metadata · audit · events"]
pg[("<b>PostgreSQL</b>")]
sinks{{"<b>Event sinks</b><br/>webhooks · brokers"}}
s3[("<b>S3 backends</b><br/>SeaweedFS · MinIO<br/>Garage · AWS")]
op -- "Connect RPC" --> cp
agent -- "MCP" --> cp
app -- "Connect RPC" --> cp
cp --> pg
cp -- "events" --> sinks
cp -- "bucket and object management" --> s3
app -. "object bytes, presigned" .-> s3
classDef client fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E
classDef ui fill:#EDE9FE,stroke:#7C3AED,color:#3B0764
classDef role fill:#DCFCE7,stroke:#16A34A,color:#14532D
classDef optional fill:#F1F5F9,stroke:#64748B,color:#334155,stroke-dasharray:5 4
classDef store fill:#FEF3C7,stroke:#D97706,color:#78350F
classDef external fill:#FCE7F3,stroke:#DB2777,color:#831843
class app,agent,op client
class cp role
class pg,s3 store
class sinks external
Access is granted through users, API tokens and capability tokens — short-lived, budgeted and individually revocable. The capability primitive is limes, a Go library of its own with no database driver and no storage SDK among its dependencies; Paladin backs its contracts with PostgreSQL. Besides the Connect API, the console and the Go and Python SDKs, the same operations are exposed over the Model Context Protocol, so AI agents can use them as tools.
Paladin is one Go binary. Each role below runs as its own Deployment
(paladin serve <role>), next to a web console:
It needs PostgreSQL and an S3-compatible store — SeaweedFS, MinIO, Garage or AWS S3.
Contents
- Deploy to Kubernetes — the Helm charts, on your cluster
- Run it with Task — from a clone: compose, a local cluster, the gates
- How changes land — trunk, CI and releases
- Documentation — the rest, by document
- Layout — where things live in the tree
- License
Deploy to Kubernetes
Two Helm charts, published to GHCR with every release (vX.Y.Z):
The charts and the images they deploy are signed keyless with Sigstore, and each image carries a signed SPDX SBOM per platform; releasing.md shows how to verify them.
The charts run neither PostgreSQL nor the object store: bring PostgreSQL 16+ and an S3-compatible store whose access key may create buckets. Kubernetes 1.25+ and Helm 3.8+.
-
Create the three database roles, once, as a superuser — the SQL is in docs/install.md.
-
Put the credentials into Secrets:
kubectl create namespace paladin kubectl -n paladin create secret generic paladin-postgres-app --from-literal=password='<app-password>' kubectl -n paladin create secret generic paladin-postgres-migrate --from-literal=password='<migrate-password>' kubectl -n paladin create secret generic paladin-s3 \ --from-literal=access_key='<access-key>' --from-literal=secret_key='<secret-key>' -
Give the backend chart what it cannot default:
# paladin-core.yaml postgres: host: postgres.databases.svc.cluster.local sslmode: require app: existingSecret: paladin-postgres-app migrate: existingSecret: paladin-postgres-migrate config: storage: backends: primary: endpoint: http://seaweedfs.storage.svc.cluster.local:8333 public_endpoint: https://s3.example.com # what clients reach the store by region: us-east-1 storage: s3CredentialsSecret: create: false existingSecret: paladin-s3 -
Install both charts:
helm install paladin-core oci://ghcr.io/oleg-tkachuk/charts/paladin-core \ --version <version> -n paladin -f paladin-core.yaml helm install paladin-console oci://ghcr.io/oleg-tkachuk/charts/paladin-console \ --version <version> -n paladin -
Sign in as
admin, with the password generated on first install:kubectl -n paladin get secret paladin-bootstrap-admin -o jsonpath='{.data.password}' | base64 -d kubectl -n paladin port-forward svc/paladin-console 3000:3000
docs/install.md covers the rest: ingress, TLS between the planes, and what to set when ArgoCD renders the charts.
Documentation
Layout
The components share a repository because a wire-contract change is a change
to all of them: the proto in proto/ is the source of truth, and the backend,
the console and both SDKs generate their stubs from it.
Compatibility is not kept across releases yet, and only main is supported —
docs/upgrading.md lists what each release breaks.
License
AGPL-3.0-only — see LICENSE and NOTICE. The API
contract in proto/ and the Go and Python SDKs in sdk/
are Apache-2.0, each under its own LICENSE, so a client can embed them
without taking on the AGPL; so is limes.