Users, sessions, keys and audit

Sessions

POST /api/auth/setup

public

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

public

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

read-only

Deletes the session and clears the cookie. Returns {"ok": true}.

GET /api/auth/me

public

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

read-only

Lists every user.

POST /api/users

admin

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}

admin

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

admin, or the user themself

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}

admin Destructive

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

read-only

[
  {"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

admin

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}

admin Destructive

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

read-only

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.