Users, sessions, keys and audit
Sessions
POST /api/auth/setup
Creates the first admin. Valid only while no user exists; afterwards it
answers 403 setup already completed. There are no default credentials.
| Body field | Rules |
|---|---|
username |
2 to 64 characters: letters, digits, ., _, -. |
password |
At least 8 characters. |
curl -sk -c cookies.txt -X POST https://appliance:8443/api/auth/setup \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"correct horse battery"}'
{"user": {"id": 1, "username": "admin", "role": "admin", "disabled": false, "created_at": "2026-08-31T00:00:12Z"}}
The response also sets the session cookie.
POST /api/auth/login
Same body as setup. Success returns {"user": …} and sets the
ovtl_session cookie. Failure is a 401 invalid credentials after a
one-second delay, identical for an unknown user and a wrong password,
and is audited as auth.login_failed. A disabled user cannot log in.
curl -sk -c cookies.txt -X POST https://appliance:8443/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"correct horse battery"}'
POST /api/auth/logout
Deletes the session and clears the cookie. Returns {"ok": true}.
GET /api/auth/me
Reports the caller’s state without requiring authentication. One of:
{"setup_required": true}
{"user": null}
{"user": {"id": 1, "username": "admin", "role": "admin", "disabled": false, "created_at": "2026-08-31T00:00:12Z"}}
A request made with an accepted API key reports a synthetic user with a
negative id and username key:<name>.
Users
The user object is {id, username, role, disabled, created_at}. The
password hash is never returned.
GET /api/users
Lists every user.
POST /api/users
| Body field | Rules |
|---|---|
username, password |
As for setup. |
role |
admin or readonly. |
Returns 201 with the user. A duplicate username is a 400.
curl -sk -X POST https://appliance:8443/api/users \
-H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
-d '{"username":"ops-viewer","password":"a long passphrase","role":"readonly"}'
PUT /api/users/{id}
Changes role and/or disabled state. Omitted fields keep their values.
| Body field | Type |
|---|---|
role |
admin or readonly |
disabled |
bool |
400 cannot demote or disable the last admin protects the last enabled
admin. 404 for an unknown user.
PUT /api/users/{id}/password
Body {"password": "…"}, at least 8 characters. An admin may set anyone’s
password; a read-only user may set only their own, which is the single
mutation the read-only role is allowed. Returns {"ok": true}.
DELETE /api/users/{id}
Deletes the user and their sessions. Refused for the last enabled admin.
Returns {"ok": true}.
API keys
Keys carry the same two roles as users. The daemon stores only a SHA-256
of the token. Keys are honoured only while the apikeys.enabled setting
is "1"; see the overview for enabling it.
GET /api/apikeys
[
{"id": 1, "name": "grafana", "role": "readonly", "created_by": "admin", "created_at": "2026-08-31T00:40:00Z", "last_used_at": "2026-09-01T07:12:00Z"},
{"id": 2, "name": "vault-script", "role": "admin", "created_by": "admin", "created_at": "2026-08-31T00:41:00Z"}
]
Tokens are never listed.
POST /api/apikeys
| Body field | Rules |
|---|---|
name |
2 to 64 characters: letters, digits, ., _, -. Unique. |
role |
admin or readonly. |
curl -sk -X POST https://appliance:8443/api/apikeys \
-H "Authorization: Bearer $OVTL_ADMIN_KEY" -H 'Content-Type: application/json' \
-d '{"name":"vault-script","role":"admin"}'
{"id": 2, "name": "vault-script", "role": "admin", "token": "ovtl_…"}
The token appears in this response and nowhere else. Store it in your secret store immediately.
DELETE /api/apikeys/{id}
Revokes the key. Requests already in flight complete; the next request
with that token is anonymous. Returns {"ok": true}; 404 for an
unknown id.
Audit
GET /api/audit
The last 200 audit entries, newest first.
[
{"id": 918, "ts": "2026-09-01T07:12:04Z", "actor": "key:vault-script", "remote_addr": "10.0.0.14:52210", "action": "job.create.export", "subject": "OVB001L5", "params": "{\"job\":42,\"remote\":1,\"generation\":\"\",\"system\":\"\",\"target_library\":0}"},
{"id": 917, "ts": "2026-09-01T07:11:50Z", "actor": "admin", "remote_addr": "10.0.0.5:61002", "action": "apikey.create", "subject": "vault-script", "params": "{\"id\":2,\"role\":\"admin\"}"}
]
params is a JSON string. Actions recorded by v1.0.0:
auth.setup, auth.login, auth.login_failed, auth.logout,
user.create, user.update, user.password, user.delete,
apikey.create, apikey.delete, settings.update, license.ack,
support.bundle, remote.create, remote.update, remote.delete,
remote.test, bucket.delete_prefix, catalog.rebuild,
job.create.export, job.create.import, job.create.evict,
job.retry, job.cancel, cart.create, cart.delete, drive.load,
drive.unload, storage.rescan, storage.setup, storage.teardown,
storage.grow, pool.create, pool.remove, library.create,
library.delete, library.apply, library.recover, target.acl.add,
target.acl.update, target.acl.remove, target.port.serving,
system.restart, system.dataplane_restart, system.reboot,
system.update, system.rollback.
The audit log is not the event journal. Events (what the appliance observed) are on the events page.