Key points
- A scope secret is not write-only.
GET /v1/secrets/{secretName}decrypts and returns the value to a caller holdingsecret.read_valueon that secret. - Listing secrets and reading a value are separate actions:
secret.list_metadatareturns names,secret.read_valuereturns the value. - Reading a secret value is audited, naming the caller and the secret.
- The credential store is different:
/v1/system/credentialsreturns metadata and version history, never a value. - Names and scopes come from the path and query; the request body carries only
value. - Writing over a GitOps-managed value makes the database the owner, and the next sync reports drift rather than restoring Git.
- A GitOps envelope from
/v1/secrets/encryptis bound to this install’s master key, which is what makes committing it safe and the key a restore dependency.
Operations
GET/v1/variablesAuthorized
Lists runtime variables in a scope.
Requires variable.list
Notes
Variable values are readable by design. Anything that should not be is a secret, not a variable.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
scope | query | string | Optional | Scope to list. Values are stored per scope, so the same name can exist in several.Default: the default scope |
include_source | query | boolean | Optional | Include whether each value came from the database or a configuration repository.Default: false |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/variables?scope=platform/production&include_source=true" | jqResponses
Variables in the requested scope.
[
{
"name": "RELEASE_CHANNEL",
"value": "stable",
"scope": "platform/production",
"source": "database",
"updated_at": "2026-08-19T09:12:44Z"
}
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable, so the list cannot be filtered. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/variables/scopesAuthorized
Lists the scopes that hold variables.
Notes
Useful before writing a value: a typo in a scope name creates a new scope silently rather than failing.
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/variables/scopes" | jqResponses
Scopes holding variables.
[
{ "scope": "platform/production" },
{ "scope": "platform/shared" }
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/variables/{variableName}Authorized
Reads one variable value.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
variableName | path | string | Required | Variable name. Must match ^[A-Za-z0-9_.-]+$. |
scope | query | string | Optional | Scope to read from. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/variables/RELEASE_CHANNEL?scope=platform/production" | jq -r .valueResponses
The variable value.
{"value":"stable"}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No variable with that name in that scope. | Check the scope: the same name in another scope is a different variable. |
| 500 | The read failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
PUT/v1/variables/{variableName}Authorized
Creates or replaces a variable in a scope.
Notes
Writing over a GitOps-managed variable makes the database the owner. The next config sync reports drift rather than silently restoring Git.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
variableName | path | string | Required | Variable name. |
scope | query | string | Optional | Scope to write into. An unknown scope is created rather than rejected. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/variables/RELEASE_CHANNEL?scope=platform/production" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":"stable"}'Responses
Stored, for both create and replace.
{"name":"RELEASE_CHANNEL","scope":"platform/production","source":"database"}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The body is not valid JSON, or the name is unusable. | Send {"value": "..."} and a name matching ^[A-Za-z0-9_.-]+$. |
| 500 | The write failed. | Retry. |
Side effects
- Marks the value as database-owned, which takes it out of GitOps management until the configuration repository is reconciled.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
DELETE/v1/variables/{variableName}Authorized
Deletes a variable from a scope.
Notes
Deleting is silent about whether anything was there. Read it first if you need to know.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
variableName | path | string | Required | Variable to delete. |
scope | query | string | Optional | Scope to delete from. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/variables/RELEASE_CHANNEL?scope=platform/production" -w "%{http_code}\n"Responses
Deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The delete failed. | Retry. |
Side effects
- Pipelines that declare the variable fail before execution until it resolves again.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/secretsAuthorized
Lists secret names and metadata in a scope.
Requires secret.list_metadata
Notes
Listing needs secret.list_metadata, which is a weaker action than reading a value. The two are granted separately on purpose.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
scope | query | string | Optional | Scope to list. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/secrets?scope=platform/production" | jqResponses
Secret metadata for the scope.
[
{
"name": "REGISTRY_TOKEN",
"scope": "platform/production",
"source": "database",
"updated_at": "2026-08-19T09:15:02Z"
}
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.goservices/nopsai/pkg/routeauthz/routeauthz.go
GET/v1/secrets/scopesAuthorized
Lists the scopes that hold secrets.
Requires secret.list_metadata
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/secrets/scopes" | jqResponses
Scopes holding secrets.
[
{ "scope": "platform/production" },
{ "scope": "platform/shared" }
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/secrets/{secretName}Authorized
Reads and decrypts one secret value.
Requires secret.read_value
Notes
This route is the reason secret.read_value exists as a separate action from secret.list_metadata and secret.write_value. Anyone holding it can recover any secret in scope.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secretName | path | string | Required | Secret name. |
scope | query | string | Optional | Scope to read from. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/secrets/REGISTRY_TOKEN?scope=platform/production" | jq -r .valueResponses
The decrypted secret value.
{"value":"<the stored secret>"}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | No secret name in the path. | Name the secret. |
| 403 | The caller does not hold secret.read_value on this secret. | This is the action that governs recovering a stored credential. Grant it deliberately. |
| 404 | No secret with that name in that scope. | Check the scope. |
| 409 | The secret exists but has no value set. | Write a value before reading one. |
| 500 | Decryption failed, which usually means the master key changed. | Restore the original NOPSAI_MASTER_KEY or rotate the secret. |
Side effects
- Writes an audit record naming the caller and the secret. Reading a secret is an audited event, not a silent one.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.goservices/nopsai/pkg/routeauthz/routeauthz.go
PUT/v1/secrets/{secretName}Authorized
Creates or replaces a secret value.
Requires secret.write_value
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secretName | path | string | Required | Secret name. Must match ^[A-Za-z0-9_.-]+$. |
scope | query | string | Optional | Scope to write into. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/secrets/REGISTRY_TOKEN?scope=platform/production" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"value\":\"$TOKEN\"}"Responses
Stored, for both create and replace.
{"name":"REGISTRY_TOKEN","scope":"platform/production","source":"database"}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The body is not valid JSON or the name is unusable. | Send {"value": "..."}. |
| 500 | Encryption or storage failed. | Retry. A failed write leaves the previous value in place. |
Side effects
- Encrypts with
NOPSAI_MASTER_KEYbefore storage. - Takes the value out of GitOps ownership until reconciled.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
DELETE/v1/secrets/{secretName}Authorized
Deletes a secret from a scope.
Requires secret.delete
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
secretName | path | string | Required | Secret to delete. |
scope | query | string | Optional | Scope to delete from. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/secrets/REGISTRY_TOKEN?scope=platform/production" -w "%{http_code}\n"Responses
Deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The delete failed. | Retry. |
Side effects
- Removes the encrypted value permanently.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
POST/v1/secrets/encryptAuthorized
Encrypts a value into a GitOps-safe envelope.
Requires secret.write_value
Notes
The envelope is bound to this install’s master key. Another install cannot decrypt it, which is what makes committing it safe and what makes the key a restore dependency.
Call it
curl -sX POST "$NOPSAI_URL/v1/secrets/encrypt" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"value\":\"$TOKEN\"}" | jq -r .encryptedResponses
The encrypted envelope.
{"encrypted":"enc:v1:9f2c..."}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The body carries no value. | Send {"value": "..."}. |
| 500 | Encryption failed. | Check that NOPSAI_MASTER_KEY is configured. |
Side effects
- Stores nothing. The envelope is returned and forgotten.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/system/credentialsAuthorized
Lists platform credentials. Metadata only.
Notes
has_value answers "is this configured?" without exposing anything. That is the whole read surface for a credential value.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
kind | query | string | Optional | Restrict to one credential kind. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials" | jq '.[] | {reference, kind, status, active_version}'Responses
Credential metadata.
[{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}]When it fails
| Status | Cause | What to do |
|---|---|---|
| 401 | No usable caller identity. | Send a token. |
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.goservices/nopsai/credential_models.go
POST/v1/system/credentialsAuthorized
Creates a platform credential.
Notes
A credential is referenced by name from everywhere else, which is what keeps provider keys out of pipeline YAML and out of profile documents.
Call it
curl -sX POST "$NOPSAI_URL/v1/system/credentials" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"reference\":\"platform/anthropic\",\"kind\":\"api_key\",\"value\":\"$API_KEY\"}" | jq '{id, reference, has_value}'Responses
Credential created.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | A missing reference, an unknown kind, or an empty value. | The message names the field. |
| 401 | No usable caller identity. | Send a token. |
| 403 | The caller may not create credentials. | Credential creation is separately authorized from reading metadata. |
| 503 | Authorization is unavailable. | Check AAA. |
Side effects
- Encrypts the value with the master key and stores it as version 1.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
GET/v1/system/credentials/{credentialID}Authorized
Reads credential metadata and version history. The value is never returned.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID" | jqResponses
Credential metadata and versions.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 403 | The caller may not read this credential. | Metadata access is authorized per credential. |
| 404 | No credential with that id. | List the credentials. |
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The versions could not be loaded. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
PUT/v1/system/credentials/{credentialID}/valueAuthorized
Rotates a credential by adding a new version.
Notes
Rotation is additive rather than destructive, which is the difference between this and a scope secret: a bad rotation is undone by activating the previous version.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID/value" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"value\":\"$NEW_KEY\"}" | jq '{active_version, last_rotated_at}'Responses
Rotated; the new version is active.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An empty value or an unusable credential id. | Send {"value": "..."}. |
Side effects
- Adds a version and activates it. Everything referencing the credential uses the new value on its next call.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.godoc/credential-management.md
POST/v1/system/credentials/{credentialID}/versions/{version}/activateAuthorized
Activates a stored credential version.
Notes
This is the rollback path. Test the provider afterwards — an older key may itself have been revoked at the provider.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
version | path | integer | Required | Credential version number. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID/versions/2/activate" | jq '{active_version}'Responses
Version activated.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | An unknown version or a malformed id. | Read the version list first. |
Side effects
- Everything referencing the credential uses this version on its next call.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
DELETE/v1/system/credentials/{credentialID}/versions/{version}Authorized
Deletes a credential version.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
version | path | integer | Required | Credential version number. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID/versions/1" -w "%{http_code}\n"Responses
Version deleted.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The version is active, or does not exist. | Activate another version before deleting this one. |
Side effects
- Removes a rollback target permanently.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
POST/v1/system/credentials/{credentialID}/enableAuthorized
Enables a disabled credential.
Notes
Enabling does not validate the value against the provider. Run the relevant test route afterwards.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID/enable" | jq '{reference, status}'Responses
Credential enabled.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}Side effects
- Resources referencing the credential resolve it again.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
POST/v1/system/credentials/{credentialID}/disableAuthorized
Disables a credential without deleting it.
Notes
The containment step for a suspected leak: disable first, rotate second, delete last. Disabling keeps the history an investigation needs.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
Call it
curl -sX POST -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID/disable" | jq '{reference, status}'Responses
Credential disabled.
{
"id": "d61a4b02-7c39-4e58-9a11-40b2c7e35f18",
"reference": "platform/anthropic",
"kind": "api_key",
"status": "enabled",
"has_value": true,
"active_version": 3,
"last_rotated_at": "2026-08-12T09:20:41Z",
"managed_by_config_repo": false
}Side effects
- Model profiles, MCP servers, and Git connections referencing it fail to resolve.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
DELETE/v1/system/credentials/{credentialID}Authorized
Deletes a credential and all its versions.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
credentialID | path | uuid | Required | Credential identifier. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/system/credentials/$CREDENTIAL_ID" -w "%{http_code}\n"Responses
Credential deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 409 | A profile, server, or connection still references the credential. | The platform refuses rather than breaking them. Repoint the dependants first. |
| 500 | The delete failed. | Retry. |
Side effects
- Removes every version permanently.
- Writes an audit record.
Proven by
services/nopsai/credential_service_test.goservices/nopsai/credential_handlers.go
GET/v1/repositories/{repoOwner}/{repoName}/secretsAuthorized
Lists repository-scoped secret names. Metadata only.
Requires secret.list_metadata
Notes
Repository-scoped values are resolved for runs triggered from that repository. A scope secret and a repository secret with the same name are different values.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/repositories/acme/payments/secrets" | jqResponses
Repository secret metadata.
[
{ "name": "DEPLOY_KEY", "repository_name": "acme/payments", "source": "database" }
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
PUT/v1/repositories/{repoOwner}/{repoName}/secrets/{secretName}Authorized
Creates or replaces a repository-scoped secret value.
Requires secret.write_value
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
secretName | path | string | Required | Secret name. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/repositories/acme/payments/secrets/DEPLOY_KEY" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"value\":\"$DEPLOY_KEY\"}"Responses
Stored.
{ "name": "DEPLOY_KEY", "repository_name": "acme/payments", "source": "database" }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The body is not valid JSON or the name is unusable. | Send {"value": "..."}. |
| 500 | Encryption or storage failed. | Retry. |
Side effects
- Encrypts with the master key before storage.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
DELETE/v1/repositories/{repoOwner}/{repoName}/secrets/{secretName}Authorized
Deletes a repository-scoped secret.
Requires secret.delete
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
secretName | path | string | Required | Secret to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/repositories/acme/payments/secrets/DEPLOY_KEY" -w "%{http_code}\n"Responses
Deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The delete failed. | Retry. |
Side effects
- Removes the encrypted value permanently.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/repositories/{repoOwner}/{repoName}/variablesAuthorized
Lists repository-scoped variables.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/repositories/acme/payments/variables" | jqResponses
Repository variables.
[
{ "name": "DEPLOY_ENVIRONMENT", "value": "staging", "repository_name": "acme/payments" }
]When it fails
| Status | Cause | What to do |
|---|---|---|
| 503 | Authorization is unavailable. | Check AAA. |
| 500 | The query failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
GET/v1/repositories/{repoOwner}/{repoName}/variables/{variableName}Authorized
Reads one repository-scoped variable value.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
variableName | path | string | Required | Variable name. |
Call it
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/repositories/acme/payments/variables/DEPLOY_ENVIRONMENT" | jq -r .valueResponses
The variable value.
{"value":"staging"}When it fails
| Status | Cause | What to do |
|---|---|---|
| 404 | No variable with that name for this repository. | A scope variable of the same name is a different value. |
| 500 | The read failed. | Platform fault. |
Side effects
- None.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
PUT/v1/repositories/{repoOwner}/{repoName}/variables/{variableName}Authorized
Creates or replaces a repository-scoped variable.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
variableName | path | string | Required | Variable name. |
Call it
curl -sX PUT "$NOPSAI_URL/v1/repositories/acme/payments/variables/DEPLOY_ENVIRONMENT" \
-H "Authorization: Bearer $NOPSAI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value":"staging"}'Responses
Stored.
{ "name": "DEPLOY_ENVIRONMENT", "repository_name": "acme/payments", "source": "database" }When it fails
| Status | Cause | What to do |
|---|---|---|
| 400 | The body is not valid JSON or the name is unusable. | Send {"value": "..."}. |
| 500 | The write failed. | Retry. |
Side effects
- Marks the value database-owned, taking it out of GitOps management until reconciled.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
DELETE/v1/repositories/{repoOwner}/{repoName}/variables/{variableName}Authorized
Deletes a repository-scoped variable.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
repoOwner | path | string | Required | Repository owner. |
repoName | path | string | Required | Repository name. |
variableName | path | string | Required | Variable to delete. |
Call it
curl -sX DELETE -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/repositories/acme/payments/variables/DEPLOY_ENVIRONMENT" -w "%{http_code}\n"Responses
Deleted.
When it fails
| Status | Cause | What to do |
|---|---|---|
| 500 | The delete failed. | Retry. |
Side effects
- Pipelines declaring the variable fail before execution until it resolves again.
- Writes an audit record.
Proven by
services/nopsai/secrets_variables_handlers_test.goservices/nopsai/secrets_variables_handlers.go
How it works
The distinction that matters for a security review is not "can secrets be read" but "who holds secret.read_value". Storing a credential here protects it from everyone without that action and from nobody with it, and every read leaves an audit record naming the reader.
Variables are readable by design, because they are configuration. If a value should not be listed by anyone who can list variables, it is a secret, not a variable — the two stores differ in exactly that respect.
A missing scope is created rather than rejected on write. A typo in a scope name produces a value nothing resolves, with no error at the time you make the mistake.
Implementation evidence
services/nopsai/secrets_variables_handlers.goVariable and secret storage, encryption, and value reads.
services/nopsai/pkg/routeauthz/routeauthz.goThe actions each secret route checks.
services/nopsai/credential_handlers.goCredential store responses, which carry metadata only.

