Nezal API
The contract is openapi.yaml. Base path /api/v1.
Authentication
- Browser:
GET /api/v1/auth/oidc/loginor/auth/saml/loginredirects to the IdP. The callback sets thenezal_sessioncookie. - CLI:
nezal loginopens the dashboard. The dashboard callsPOST /auth/cli/tokenand hands a 30-day token to the CLI's localhost callback. Send it asAuthorization: Bearer. - First run:
NEZAL_BOOTSTRAP_TOKENacts as an admin until SSO is configured. - Local accounts:
POST /auth/password/loginwith email and password. It answersokormfa_requiredormfa_enrol_required. Ten failed attempts on one account in fifteen minutes locks it for fifteen. Sixty from one address blocks the address.
Things a client has to get right
CSRF. A cookie-authenticated request that changes anything must carry an Origin or
Referer the API recognises. Bearer-token clients are unaffected.
Side-effecting GETs refuse the cookie entirely (terminal and audit export as well as
logs). A browser will issue those cross-origin so a cookie there would be
exploitable from any page. Use a bearer token or mint a single-use 60-second ticket with
POST /auth/ticket and pass it as a query parameter.
Sessions are revocable. Every token carries a jti that is checked against the sessions
table on each request so revocation takes effect immediately rather than at expiry. The
endpoints are GET /me/sessions and DELETE /me/sessions/{jti} and
POST /me/sessions/revoke-all (which spares the calling session).
Empty lists are [] and never null. One that is not is a bug.
Errors
{"code": "nezal.env.quota.perUser", "params": {"owner": "amr", "current": "3", "max": "3"}}
Admission failures carry causes[] with one entry per rule. Each entry has field and
code and params. Render texts in English and Arabic from api/pkg/i18n
(Render(lang, code, params)).
Licence
PUT /licence {"key": "NZL2...."} installs a key. NZL1. keys are still accepted. States:
valid and grace and expired and invalid and missing. Issue keys with
nezal-licence issue (vendor only).
Expired or missing refuses environment creation and extension and sync. Nothing else:
waking and previews and sign-in all keep working and so does the audit log. A licensed
feature that is absent answers 402 with the feature name in params. A client should
render that as "not in your plan" rather than as an error. GET /licence/usage-report
returns a signed report of monthly active seats.
Audit
GET /audit(admin)GET /audit/verifyGET /audit/export(signed evidence package)PUT /settings/siemfor syslog or HTTP forwarding
Entries are hash-chained and can never be altered or deleted.