Admin API
The admin API lives on the admin port (:9001 by default) under the /_jay
prefix. Every endpoint requires the admin bearer token:
Authorization: Bearer <JAY_ADMIN_TOKEN>This port should never face an untrusted network. It mints credentials.
Endpoints
Section titled “Endpoints”| Endpoint | Method | Description |
|---|---|---|
/_jay/accounts |
POST | Create an account |
/_jay/tokens |
POST | Create a token |
/_jay/tokens |
GET | List tokens |
/_jay/tokens/{id} |
DELETE | Revoke a token |
/_jay/metrics |
GET | Server metrics |
/_jay/presign |
POST | Generate a presigned URL |
/_jay/quarantine |
GET | List quarantined objects |
/_jay/quarantine/revalidate |
POST | Revalidate a quarantined object |
/_jay/quarantine |
DELETE | Purge quarantined objects |
/_jay/buckets/{name} |
GET | Visibility and policy of one bucket |
/_jay/buckets/{name}/policy |
PUT | Install or replace the bucket policy |
/_jay/buckets/{name}/policy |
DELETE | Remove the bucket policy |
/_jay/buckets/{name}/visibility |
PUT | Switch between private and public-read |
Token creation and presigning are covered in Authentication.
Revoking a token takes effect immediately, including for connections already authenticated: revocation is re-checked on every cached auth hit.
Bucket policy and visibility
Section titled “Bucket policy and visibility”These two are what decide who can reach a bucket, so they live here rather than on the S3 port: Jay’s policy dialect is not AWS’s, and visibility has no S3 operation at all. See S3 compatibility for the reasoning, and Authentication for what a policy document means.
The request body of PUT .../policy is the policy document — no envelope,
so the file you edit is the file you send. Up to 64 KiB.
curl -X PUT http://localhost:9001/_jay/buckets/mybucket/policy \ -H "Authorization: Bearer $JAY_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ --data-binary @policy.json
# → {"bucket":"mybucket","statements":2}The reply counts the statements rather than saying ok. On an access-control
change, “it worked” with nothing behind it is worth very little; the count is
the cheapest proof the document was parsed and not merely stored.
curl -X PUT http://localhost:9001/_jay/buckets/mybucket/visibility \ -H "Authorization: Bearer $JAY_ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"visibility":"public-read"}'Both take effect on the next request: nothing caches a bucket record.
The document is validated on the way in
Section titled “The document is validated on the way in”A bucket policy that parses can still be inert, and an inert access-control
document is the worst kind — it looks installed and does nothing. So these are
400 at the door instead of a surprise months later:
| Refused | Why it would otherwise be silent |
|---|---|
| An action outside the action list | A misspelt object:read matches nothing, so the deny that was meant to close a prefix leaves it open |
An effect that is not allow or deny |
Matches nothing |
An empty actions or subjects |
Matches nothing. Use ["*"] for “any authenticated token” |
| A policy with no statements | Grants and denies nothing. Use DELETE if that is the intent |
A malformed CIDR in ip_whitelist |
The dangerous one: an entry that does not parse is dropped, and an empty whitelist matches every address — so 10.0.0/8, one dot short, turns an internal-only grant into an internet-wide one |
A refused document is not stored, so the previous policy stays in force.
Removing a policy closes a bucket rather than opening one: with no policy there is nothing to grant on, and the default is deny.
jay-admin
Section titled “jay-admin”jay-admin ships in the release archives and in the container image, and wraps
the same endpoints.
export JAY_ADMIN_TOKEN=my-secret-admin-token
jay-admin create-account -name myappjay-admin create-token -account ACCOUNT_ID -name deployjay-admin list-tokensjay-admin revoke-token -id TOKEN_IDjay-admin metricsjay-admin presign -bucket mybucket -key file.txt -token-id TOKEN_IDjay-admin presign -bucket mybucket -key file.txt -token-id TOKEN_ID \ -style aws -host s3.example.comjay-admin quarantine-listjay-admin quarantine-purge
jay-admin get-bucket -bucket mybucketjay-admin set-bucket-policy -bucket mybucket -file policy.jsonjay-admin delete-bucket-policy -bucket mybucketjay-admin set-bucket-visibility -bucket mybucket -visibility public-readset-bucket-policy reads the document from a file, or from standard input with
-file -, and sends those bytes unchanged — so an error names a line that
exists in the file you wrote.
Quarantine
Section titled “Quarantine”Quarantined objects are ones where metadata and disk disagree — a record with no file, or a file with no record — or where the scrubber found a digest that no longer matches the bytes.
Jay never deletes an inconsistency on its own. An inconsistency is the only evidence of whatever went wrong, and deleting it destroys the trace. Purging is an explicit operator action.
revalidate re-checks a quarantined object and restores it if it now verifies.
jay-rekey
Section titled “jay-rekey”JAY_SIGNING_SECRET is the AES-GCM key that encrypts token secrets in bbolt.
Changing it directly makes every stored token unreadable. jay-rekey
re-encrypts them under a new key instead.