Platform

Platform overviewArchitectureWorkflow orchestrationGitOps configurationGovernance and AAAAI and MCPKnowledge and contextRuntime and executionEvidence and monitoring

Use cases

All use casesProduction incidentRelease preparationHotfix to productionSecurity scan triage
Why NopsAIIntegrationsSecurity

Resources

All resourcesAI agent governanceMCP governanceMCP securitySelf-hosted platforms
PricingGitHub

Company

How a run worksAboutContactBook a demo

Identity administration API

Accounts, service accounts, tokens, roles, identity providers, and the audit log that records what they all did.

ReferenceAdministratorSecurity

Key points

  • Every route here is administrator-gated.
  • Creating a user answers with the whole user list rather than the created record.
  • A token carries its account’s permissions at request time, so rebinding a role takes effect without reissuing tokens.
  • Disabling a service account is the fastest kill switch for an integration: one call instead of one per token.
  • Local login can only be turned off while external auth is enabled and a provider remains enabled — the platform refuses to lock itself out.
  • Changing a live provider’s issuer re-identifies its users, because a subject is scoped by issuer.
  • The audit log is where a secret read appears, naming who recovered it.

Operations

GET/v1/admin/usersAdministrator

Lists user accounts.

Notes

external_managed: true means the provider owns the record: password and email changes belong there, not here.

Call it

List usersapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/users" | jq '.[] | {sub, provider, status, external_managed}'
Result

Every account with its provider, status, and whether the identity provider owns it.

Responses

200application/json

All user accounts.

[
  {
    "id": "5b0a...",
    "sub": "admin",
    "email": "[email protected]",
    "provider": "local",
    "status": "active",
    "roles": [{ "role": "nopsai-admin" }],
    "external_managed": false,
    "authentication_source": "local"
  }
]

When it fails

StatusCauseWhat to do
500The user query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_user_handlers.go
  • services/nopsai/auth_models.go
POST/v1/admin/usersAdministrator

Creates a local user account.

Notes

It answers with the whole user list, not the new account, so a client that expects a single object gets an array.

Call it

Create a local userapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/users" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sub":"casey","email":"[email protected]","password":"<initial>","role":"pipeline-operator"}' | jq
Result

The refreshed user list — this route answers with the list rather than the created record.

Replace before running
  • sub is the identity. Choose it deliberately: email is metadata and can change, the subject cannot.

Responses

200application/json

The user list after the create.

[
  {
    "id": "5b0a...",
    "sub": "admin",
    "email": "[email protected]",
    "provider": "local",
    "status": "active",
    "roles": [{ "role": "nopsai-admin" }],
    "external_managed": false,
    "authentication_source": "local"
  }
]

When it fails

StatusCauseWhat to do
400A missing subject, an unusable password, or an unknown role.The message names the field.
409The subject or email already exists.Subjects are unique; reuse the existing account.
500The account or its role binding could not be saved.The create is transactional: retry, nothing partial was stored.

Side effects

  • Creates the account and any role binding in one transaction.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_user_handlers.go
PUT/v1/admin/users/{userID}Administrator

Replaces a user record.

Parameters

NameInTypeRequiredDescription
userIDpathstringRequiredUser identifier.

Call it

Replace a user recordapi-identity-administration request
curl -sX PUT "$NOPSAI_URL/v1/admin/users/$USER_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","status":"active"}' -w "%{http_code}\n"
Result

204. Setting password here resets it without knowing the current one.

Responses

204

User updated.

When it fails

StatusCauseWhat to do
400An unusable status or password.The message names the field.
404No user with that id.Confirm the id from the list.
409The email belongs to another account.Resolve the duplicate first.
500The update failed.Retry.

Side effects

  • An administrator password reset takes effect immediately and does not require the current password.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_user_handlers.go
PATCH/v1/admin/users/{userID}Administrator

Partially updates a user record.

Notes

Disable rather than delete when someone leaves: the audit trail keeps naming a subject that still resolves.

Parameters

NameInTypeRequiredDescription
userIDpathstringRequiredUser identifier.

Call it

Disable an accountapi-identity-administration request
curl -sX PATCH "$NOPSAI_URL/v1/admin/users/$USER_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"disabled"}' -w "%{http_code}\n"
Result

204. The account stops authenticating; its tokens stop working with it.

Responses

204

User updated.

When it fails

StatusCauseWhat to do
400An unusable field value.The message names the field.
404No user with that id.Confirm the id.
500The update failed.Retry.

Side effects

  • Disabling stops the account authenticating without deleting its history.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_user_handlers.go
DELETE/v1/admin/users/{userID}Administrator

Deletes a user account.

Parameters

NameInTypeRequiredDescription
userIDpathstringRequiredUser to delete.

Call it

Delete a userapi-identity-administration request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/users/$USER_ID" -w "%{http_code}\n"
Result

204. Their tokens stop working and their grants stop resolving.

Responses

204

User deleted.

When it fails

StatusCauseWhat to do
400The id is malformed.Use an id from the list.
404No user with that id.It may already be deleted.
500The delete failed.Retry.

Side effects

  • Removes the account. Audit records naming the subject remain but no longer resolve to an account.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_user_handlers.go
GET/v1/admin/service-accountsAdministrator

Lists service accounts used for automation.

Notes

last_used_at finds the automation nobody remembers configuring — usually the account worth revoking.

Call it

List service accountsapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/service-accounts" | jq '.[] | {sub, token_count, last_used_at}'
Result

Each account with how many tokens it holds and when one was last used.

Responses

200application/json

All service accounts.

[
  {
    "id": "8e3c...",
    "sub": "release-bot",
    "email": "[email protected]",
    "provider": "local",
    "status": "active",
    "token_count": 1,
    "roles": [{ "role": "pipeline-operator" }]
  }
]

When it fails

StatusCauseWhat to do
405A method other than GET.Use GET.
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
POST/v1/admin/service-accountsAdministrator

Creates a service account.

Notes

A service account is how a system gets its own identity. Using a person’s personal token instead works until that person leaves.

Call it

Create a service account with its first tokenapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/service-accounts" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sub":"release-bot","email":"[email protected]","role":"pipeline-operator","token_name":"ci","expires_in_days":365}' | jq
Result

The account and its first token. The token value appears here and nowhere else.

Responses

201application/json

Account created with a token.

{
  "service_account":  {
    "id": "8e3c...",
    "sub": "release-bot",
    "email": "[email protected]",
    "provider": "local",
    "status": "active",
    "token_count": 1,
    "roles": [{ "role": "pipeline-operator" }]
  },
  "token": {
  "id": "1c77...",
  "name": "ci",
  "token": "nopsat_...only-returned-once",
  "token_suffix": "1c77",
  "created_at": "2026-08-19T11:20:00Z",
  "expires_at": "2027-08-19T11:20:00Z"
}
}

When it fails

StatusCauseWhat to do
400A missing subject or an unusable expiry.Choose exactly one expiry form.
409The subject already exists.Reuse the account and issue another token instead.
500The account could not be created.Retry.

Side effects

  • Creates the account, its role binding, and one token in a single call.
  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
PUT/v1/admin/service-accounts/{serviceAccountID}Administrator

Replaces a service account record.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account identifier.

Call it

Replace a service account recordapi-identity-administration request
curl -sX PUT "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","status":"active"}' -w "%{http_code}\n"
Result

204.

Responses

204

Service account updated.

When it fails

StatusCauseWhat to do
400An unusable status.The message names the field.
404No service account with that id.Confirm the id.
500The update failed.Retry.

Side effects

  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
PATCH/v1/admin/service-accounts/{serviceAccountID}Administrator

Partially updates a service account.

Notes

Disabling the account is the fastest kill switch for a misbehaving integration: one call instead of one per token.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account identifier.

Call it

Disable a service accountapi-identity-administration request
curl -sX PATCH "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"disabled"}' -w "%{http_code}\n"
Result

204. Every token it holds stops working at once.

Responses

204

Service account updated.

When it fails

StatusCauseWhat to do
400An unusable status.The message names the field.
404No service account with that id.Confirm the id.
500The update failed.Retry.

Side effects

  • Disabling stops every token the account holds without revoking them individually.
  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
DELETE/v1/admin/service-accounts/{serviceAccountID}Administrator

Deletes a service account.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account to delete.

Call it

Delete a service accountapi-identity-administration request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID" -w "%{http_code}\n"
Result

204, with its tokens.

Responses

204

Deleted.

When it fails

StatusCauseWhat to do
404No service account with that id.It may already be deleted.
500The delete failed.Retry.

Side effects

  • Removes the account and every token issued to it.
  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
GET/v1/admin/service-accounts/{serviceAccountID}/tokensAdministrator

Lists tokens issued to a service account.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account identifier.

Call it

List an account’s tokensapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID/tokens" | jq
Result

Metadata only: id, name, suffix, expiry, and last use. Values are never listed.

Responses

200application/json

Token metadata.

[
  {
    "id": "1c77...",
    "name": "ci",
    "token_suffix": "1c77",
    "created_at": "2026-08-19T11:20:00Z",
    "last_used_at": "2026-08-19T12:44:10Z"
  }
]

When it fails

StatusCauseWhat to do
404No service account with that id.Confirm the id.
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
POST/v1/admin/service-accounts/{serviceAccountID}/tokensAdministrator

Issues a service account token. The value is returned once.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account identifier.

Call it

Issue another tokenapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID/tokens" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-secondary","expires_in_days":90}' | jq -r .token
Result

The token value, returned once. Issue a second token before revoking the first to rotate without downtime.

Responses

201application/json

Token issued.

{
  "id": "1c77...",
  "name": "ci",
  "token": "nopsat_...only-returned-once",
  "token_suffix": "1c77",
  "created_at": "2026-08-19T11:20:00Z",
  "expires_at": "2027-08-19T11:20:00Z"
}

When it fails

StatusCauseWhat to do
400A missing name or conflicting expiry fields.Choose exactly one expiry form.
404No service account with that id.Confirm the id.
500The token could not be stored.Retry; no token was issued.

Side effects

  • Creates a credential carrying the account’s permissions.
  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/auth_models.go
DELETE/v1/admin/service-accounts/{serviceAccountID}/tokens/{tokenID}Administrator

Revokes a service account token.

Parameters

NameInTypeRequiredDescription
serviceAccountIDpathstringRequiredService account identifier.
tokenIDpathstringRequiredToken id from the token list — not the token value.

Call it

Revoke one tokenapi-identity-administration request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" \
  "$NOPSAI_URL/v1/admin/service-accounts/$SERVICE_ACCOUNT_ID/tokens/$TOKEN_ID" -w "%{http_code}\n"
Result

204. That token stops working immediately; the account and its other tokens are unaffected.

Responses

204

Token revoked.

When it fails

StatusCauseWhat to do
404No such token for that account.Confirm both ids.
500The revocation failed.Retry; the token stays valid until it succeeds.

Side effects

  • Invalidates the token immediately.
  • Writes an audit record.

Proven by

  • services/nopsai/personal_tokens_test.go
  • services/nopsai/personal_tokens.go
GET/v1/admin/rolesAdministrator

Lists product roles.

Call it

List rolesapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/roles" | jq
Result

Every role and the object/action pairs it permits.

Responses

200application/json

Product roles.

[
  {
    "role": "pipeline-operator",
    "name": "Pipeline operator",
    "obj": "pipeline",
    "act": "run",
    "effect": "allow"
  }
]

When it fails

StatusCauseWhat to do
405A method other than GET.Use GET.
500The query failed.Platform fault.

Side effects

  • None.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
POST/v1/admin/rolesAdministrator

Creates a product role.

Notes

A role permits an action on a kind of object. Which concrete resources it reaches is decided by grants, not by the role.

Call it

Create a roleapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role":"pipeline-operator","name":"Pipeline operator","obj":"pipeline","act":"run","effect":"allow"}' | jq
Result

The role, now available to bind to users and service accounts.

Responses

201application/json

Role created.

{
  "role": "pipeline-operator",
  "name": "Pipeline operator",
  "obj": "pipeline",
  "act": "run",
  "effect": "allow"
}

When it fails

StatusCauseWhat to do
400A missing role name, object, action, or effect.All four are required.
500The role could not be saved.Retry.

Side effects

  • Adds a role subjects can be bound to.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
DELETE/v1/admin/rolesAdministrator

Deletes a product role.

Notes

Deleting a role silently narrows everyone bound to it. Check bindings before removing one.

Call it

Delete a roleapi-identity-administration request
curl -sX DELETE "$NOPSAI_URL/v1/admin/roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"role":"pipeline-operator"}' -w "%{http_code}\n"
Result

204. The role is named in the body, not the path.

Responses

204

Role deleted.

When it fails

StatusCauseWhat to do
400The body names no role.Send {"role": "..."}.
404No such role.Confirm the name from the list.
500The delete failed.Retry.

Side effects

  • Subjects bound to the role lose what it permitted.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
POST/v1/admin/user-rolesAdministrator

Grants a role to a user.

Call it

Bind a role to a userapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/user-roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"5b0a...","role":"pipeline-operator"}' | jq
Result

The binding. It takes effect on the user’s next request.

Responses

201application/json

Role bound to the user.

{
  "user_id": "5b0a...",
  "role": "pipeline-operator"
}

When it fails

StatusCauseWhat to do
400A missing user or role.Both fields are required.
404The user or role does not exist.Confirm both.
500The binding failed.Retry.

Side effects

  • Changes what the user may do.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
DELETE/v1/admin/user-rolesAdministrator

Removes a role from a user.

Call it

Unbind a roleapi-identity-administration request
curl -sX DELETE "$NOPSAI_URL/v1/admin/user-roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"5b0a...","role":"pipeline-operator"}' -w "%{http_code}\n"
Result

204.

Responses

204

Binding removed.

When it fails

StatusCauseWhat to do
400A missing user or role.Both fields are required.
404No such binding.It may already be removed.
500The removal failed.Retry.

Side effects

  • Narrows what the user may do immediately.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
POST/v1/admin/service-account-rolesAdministrator

Grants a role to a service account.

Notes

A token carries the account’s permissions at request time, not at issue time — which is why rebinding roles does not require rotating tokens.

Call it

Bind a role to a service accountapi-identity-administration request
curl -sX POST "$NOPSAI_URL/v1/admin/service-account-roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"service_account_id":"8e3c...","role":"pipeline-operator"}' | jq
Result

The binding. Existing tokens pick it up without being reissued.

Responses

201application/json

Role bound.

{
  "service_account_id": "8e3c...",
  "role": "pipeline-operator"
}

When it fails

StatusCauseWhat to do
400A missing account or role.Both fields are required.
404The account or role does not exist.Confirm both.
500The binding failed.Retry.

Side effects

  • Existing tokens gain the permission without reissue.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
DELETE/v1/admin/service-account-rolesAdministrator

Removes a role from a service account.

Call it

Unbind a service account roleapi-identity-administration request
curl -sX DELETE "$NOPSAI_URL/v1/admin/service-account-roles" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"service_account_id":"8e3c...","role":"pipeline-operator"}' -w "%{http_code}\n"
Result

204. Every token for the account loses the permission at once.

Responses

204

Binding removed.

When it fails

StatusCauseWhat to do
400A missing account or role.Both fields are required.
404No such binding.It may already be removed.
500The removal failed.Retry.

Side effects

  • Narrows every token the account holds.
  • Writes an audit record.

Proven by

  • services/nopsai/admin_roles_compat_test.go
  • services/nopsai/admin_role_handlers.go
GET/v1/admin/identity-providersAdministrator

Lists configured identity providers.

Notes

has_client_credential is how you check a provider is configured without the API ever returning the secret.

Call it

List providers with their configurationapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/identity-providers" | jq
Result

The administrative view, including whether each provider holds a client credential. The credential itself is never returned.

Responses

200application/json

Providers with their settings.

{
  "local_enabled": true,
  "oidc_enabled": true,
  "providers": [
    {
      "id": "keycloak",
      "type": "oidc",
      "display_name": "Corporate SSO",
      "has_client_credential": true
    }
  ]
}

When it fails

StatusCauseWhat to do
405A method other than GET or PUT.Use GET to read, PUT to replace.

Side effects

  • None.

Proven by

  • services/nopsai/auth_oidc_store_test.go
  • services/nopsai/auth_oidc_models.go
PUT/v1/admin/identity-providersAdministrator

Replaces the whole identity provider configuration.

Notes

local_enabled: false is accepted only when external auth is enabled and at least one provider remains enabled.

Call it

Replace provider configurationapi-identity-administration request
curl -sX PUT "$NOPSAI_URL/v1/admin/identity-providers" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @providers.json | jq
Result

The stored configuration.

Replace before running
  • providers.json carries local_enabled, oidc_enabled, and the provider list.

Responses

200application/json

Configuration replaced.

{
  "local_enabled": true,
  "oidc_enabled": true,
  "providers": []
}

When it fails

StatusCauseWhat to do
400Turning local login off while no identity provider remains enabled.The platform refuses a configuration that would lock everyone out.
405An unsupported method.Use GET or PUT.

Side effects

  • Changes how everyone signs in.
  • Writes an audit record.

Proven by

  • services/nopsai/auth_oidc_store_test.go
  • services/nopsai/auth_oidc_handlers.go
PUT/v1/admin/identity-providers/{provider}Administrator

Creates or replaces one identity provider.

Notes

Changing the issuer of a live provider re-identifies its users: the subject is scoped by issuer, so existing accounts stop matching.

Parameters

NameInTypeRequiredDescription
providerpathstringRequiredProvider id.

Call it

Configure one providerapi-identity-administration request
curl -sX PUT "$NOPSAI_URL/v1/admin/identity-providers/keycloak" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @keycloak.json | jq
Result

The stored provider. Identity is provider id, issuer, and subject — changing the issuer changes who existing users are.

Replace before running
  • keycloak.json carries the issuer, client id, credential reference, scopes, and mapping.

Responses

200application/json

Provider stored.

{
  "id": "keycloak",
  "type": "oidc",
  "display_name": "Corporate SSO",
  "has_client_credential": true
}

When it fails

StatusCauseWhat to do
400An unreachable issuer, a missing client credential, or an invalid mapping.The message names the field.
405An unsupported method.Use PUT or DELETE.

Side effects

  • Changes how users of this provider authenticate.
  • Writes an audit record.

Proven by

  • services/nopsai/auth_oidc_store_test.go
  • services/nopsai/auth_oidc_handlers.go
DELETE/v1/admin/identity-providers/{provider}Administrator

Removes an identity provider.

Parameters

NameInTypeRequiredDescription
providerpathstringRequiredProvider to remove.

Call it

Remove a providerapi-identity-administration request
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/admin/identity-providers/keycloak" -w "%{http_code}\n"
Result

Users of that provider can no longer sign in. Their accounts remain.

Responses

204

Provider removed.

When it fails

StatusCauseWhat to do
400Removing the last enabled provider while local login is off.Enable local login first, or the install locks itself out.
405An unsupported method.Use PUT or DELETE.

Side effects

  • Users of the provider lose their sign-in path; their accounts and grants remain.
  • Writes an audit record.

Proven by

  • services/nopsai/auth_oidc_store_test.go
  • services/nopsai/auth_oidc_handlers.go
GET/v1/auditAdministrator

Reads the audit log.

Notes

This is where a secret read shows up. If you want to know who recovered a credential, this is the record that says so.

Parameters

NameInTypeRequiredDescription
limitqueryintegerOptionalHow many records to return.

Call it

Read recent audit recordsapi-identity-administration request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/audit?limit=50" | jq
Result

Recent decisions and mutations, each naming the actor, the action, and the resource.

Responses

200application/json

Audit records, newest first.

[
  {
    "actor_type": "user",
    "actor_id": "admin",
    "action": "secret.read_value",
    "resource_type": "secret",
    "resource_id": "platform/production:REGISTRY_TOKEN",
    "decision": "allow",
    "created_at": "2026-08-19T12:31:02Z"
  }
]

When it fails

StatusCauseWhat to do
500The audit query failed.Platform fault.

Side effects

  • None. Reading the audit log is itself audited by the request chain.

Proven by

  • services/nopsai/auth_middleware_test.go
  • services/nopsai/routes.go
  • services/nopsai/pkg/audit

How it works

Users and service accounts are the same kind of thing to the authorization layer: a subject with roles and grants. The difference is operational — a person leaves, a system does not — which is why an integration should hold a service account token rather than a personal one.

Roles and grants answer different halves of one question. A role permits an action on a kind of object; a grant decides which concrete resources it reaches. Neither alone tells you whether a caller can do something, which is what effective permissions are for.

Prefer disabling to deleting. A disabled account stops authenticating while its audit trail still resolves to a real subject; a deleted one leaves records naming an account nobody can look up.

Implementation evidence

  • services/nopsai/admin_user_handlers.go

    User and service account handlers.

  • services/nopsai/admin_role_handlers.go

    Role definitions and bindings.

  • services/nopsai/auth_oidc_handlers.go

    Identity provider configuration.