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

First-install setup API

The unauthenticated routes that diagnose and complete a first install, and the one write that runs exactly once.

ReferenceAdministratorDeveloperOperator

Key points

  • Only /v1/setup/preflight is public. The rest of this surface stays reachable while the workspace is locked but still requires a token — the bootstrap administrator signs in first, then runs the wizard.
  • Normal authenticated routes stay locked until POST /v1/setup/bootstrap succeeds once.
  • /v1/setup/preflight names what is blocking, and a check may carry suggested_env with the variables that would resolve it.
  • Template routes render the GitOps seed without writing anything; the archive route returns the same files as a zip.
  • Bootstrap works exactly once, and needs a token but no AAA resource check: there is nothing configured yet to check against.

Operations

GET/v1/setup/preflightPublic

Reports what still blocks setup, including a database that is still starting.

Notes

Public because a stuck install has no working login, so diagnosis cannot require one. GET /v1/setup/license is public for a different reason: terms nobody was shown are worth little.

Call it

Read preflightapi-setup request
curl -s "$NOPSAI_URL/v1/setup/preflight" | jq
Result

A check list with a ready flag. Every required check with status error is a blocker.

Replace before running
  • $NOPSAI_URL is the API address, http://localhost:8080 on a local install.

Responses

200application/json

Preflight ran. Read ready rather than the status code when the platform is already serving.

{
  "ready": true,
  "can_login": true,
  "mode": "ready",
  "config_path": "/app/config.yml",
  "env_file_path": "/app/.env",
  "checks": [
    {
      "id": "database",
      "label": "Database",
      "status": "ok",
      "message": "database is reachable",
      "required": true
    }
  ]
}
503application/json

Preflight-only mode, and the platform is not ready yet. The body is the same document.

When it fails

StatusCauseWhat to do
503A required check is failing, most often a database that has not finished starting.Read checks[].message. A check may carry suggested_env, which names the variables that would resolve it.

Side effects

  • None. Preflight inspects configuration and connectivity; it changes nothing.

Proven by

  • services/nopsai/setup_wizard_test.go
  • services/nopsai/enterprise_gates_test.go
  • services/nopsai/setup_preflight.go
GET/v1/setup/statusAuthenticated

Whether first-install setup has already completed.

Call it

Check whether setup is doneapi-setup request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/setup/status" | jq '{completed, profile, counts}'
Result

completed: true once the one-time bootstrap has run.

Responses

200application/json

Setup state, what was seeded, and the checks the wizard evaluates.

{
  "completed": true,
  "completed_at": "2026-08-19T10:12:44Z",
  "profile": "local",
  "runtime_env": "local",
  "counts": {},
  "checks": [],
  "starter_profiles": []
}

When it fails

StatusCauseWhat to do
500Setup status could not be built, which usually means the database is unreachable.Check /v1/setup/preflight first — it names the failing dependency.

Side effects

  • None.

Proven by

  • services/nopsai/setup_wizard_test.go
  • services/nopsai/setup_wizard_handlers.go
  • services/nopsai/setup_wizard.go
GET/v1/setup/templatesAuthenticated

GitOps seed templates offered by the wizard.

Notes

There is no route-specific failure: an unrecognised profile is normalised to a known one rather than rejected, so check the profile in the response rather than expecting a 400.

Parameters

NameInTypeRequiredDescription
profilequerystringOptionalWhich starter profile to render templates for. An unknown value is normalised rather than rejected.
repositoriesquerystringOptionalRepositories to seed configuration for, shaping the generated team and trigger documents.Repeatable

Call it

Render the seed templatesapi-setup request
curl -s -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/setup/templates?profile=local" | jq '.files[].path'
Result

The paths of every file the wizard would seed into a configuration repository.

Responses

200application/json

The profile that was resolved and the files it renders.

{
  "profile": "local",
  "files": [
    { "path": "setting/system/auth.yaml", "content": "..." }
  ]
}

Side effects

  • None. Rendering a template writes nothing.

Proven by

  • services/nopsai/setup_wizard_test.go
  • services/nopsai/setup_wizard_handlers.go
  • services/nopsai/setup_wizard_templates.go
GET/v1/setup/templates.zipAuthenticated

Same templates as a downloadable archive.

Notes

Same normalisation as the JSON route: no route-specific failure, and an unknown profile yields the default rather than an error.

Parameters

NameInTypeRequiredDescription
profilequerystringOptionalStarter profile to render, as for the JSON route.
repositoriesquerystringOptionalRepositories to seed configuration for.Repeatable

Call it

Download the seed archiveapi-setup request
curl -s -o nopsai-templates.zip -H "Authorization: Bearer $NOPSAI_TOKEN" "$NOPSAI_URL/v1/setup/templates.zip?profile=local"
unzip -l nopsai-templates.zip
Result

A zip containing the same files the JSON route lists, ready to commit into a configuration repository.

Responses

200application/zip

Zip archive of the rendered templates.

Side effects

  • None.

Proven by

  • services/nopsai/setup_wizard_test.go
  • services/nopsai/setup_wizard_handlers.go
POST/v1/setup/bootstrapAuthenticated

Runs the one-time bootstrap. Normal authenticated routes stay locked until this succeeds once.

Notes

It works exactly once. It needs a token — the bootstrap administrator’s — but no AAA resource check, because there is nothing configured yet to check against.

Call it

Run the bootstrapapi-setup request
curl -sX POST "$NOPSAI_URL/v1/setup/bootstrap" \
  -H "Authorization: Bearer $NOPSAI_TOKEN" \
  -H "Content-Type: application/json" \
  --data @bootstrap.json | jq
Result

The resulting setup status. Re-reading /v1/setup/status afterwards reports completed: true.

Replace before running
  • bootstrap.json carries the wizard answers: the chosen profile, the administrator, the starter profiles to seed, and the GitOps repositories.

Responses

200application/json

Bootstrap applied. The response is the same document /v1/setup/status returns.

{
  "completed": true,
  "completed_at": "2026-08-19T10:12:44Z",
  "profile": "local",
  "runtime_env": "local",
  "env_file_path": "/app/.env",
  "counts": {},
  "checks": [],
  "starter_profiles": []
}

When it fails

StatusCauseWhat to do
400The payload is malformed, or an answer is not valid for the chosen profile.The message names the field. Apply errors deliberately include the actionable write or configuration reason.
500Local secret generation or a configuration write failed.Read the message: it names the path or setting that could not be written, which is usually a permissions problem in the mounted config volume.

Side effects

  • Creates the first administrator and marks setup complete.
  • May generate local secrets and write them to the env file the response names.
  • May seed the GitOps layout and repository teams for the chosen profile.

Proven by

  • services/nopsai/setup_wizard_test.go
  • services/nopsai/setup_wizard_handlers.go
  • services/nopsai/bootstrap_schema.go
GET/v1/setup/licensePublic

The licence notice, its version and digest, and whether this installation has accepted it.

Notes

Public on purpose. Container images are freely pullable and already carry this exact text, and an administrator has to be able to read terms before accepting them.

Call it

Read the notice and the acceptance stateapi-setup request
curl -s "$NOPSAI_URL/v1/setup/license" | jq '{document_version, document_sha256, accepted, accepted_at}'
Result

The full notice text plus accepted: false on a fresh install.

Responses

200application/json

The notice text, its identity, and the current acceptance record.

{
  "text": "NopsAI Licence\n...",
  "document_version": "2026-02",
  "document_sha256": "13173227932dbde8...",
  "accepted": false
}

When it fails

StatusCauseWhat to do
500The acceptance state could not be read from the database.Check database connectivity. Acceptance is never assumed when it cannot be evaluated.

Side effects

  • None. Reading the notice records nothing.

Proven by

  • services/nopsai/setup_license_test.go
  • contract/license_notice_test.go
  • services/nopsai/setup_license.go
  • pkg/licensenotice/licensenotice.go
POST/v1/setup/license/acceptAuthenticated

Record an administrator’s acceptance of the licence notice, which setup completion requires.

Notes

Until this is recorded, POST /v1/setup/bootstrap answers 412 and the first-install gate keeps the rest of the API locked, so an installation that never accepts never becomes usable.

Call it

Accept the notice currently servedapi-setup request
curl -s -X POST -H "Authorization: Bearer $NOPSAI_TOKEN" -H "Content-Type: application/json" -d '{"accept": true, "document_sha256": "$DIGEST"}' "$NOPSAI_URL/v1/setup/license/accept"
Result

accepted: true with the recording timestamp and administrator.

Responses

200application/json

Acceptance recorded against the notice version and digest.

{
  "accepted": true,
  "accepted_at": "2026-08-23T10:00:00Z",
  "accepted_by": "admin",
  "document_version": "2026-01"
}

When it fails

StatusCauseWhat to do
400The request did not set accept: true.Acceptance must be explicit; there is no implicit path.
409The supplied digest does not match the notice the server is serving.Re-read GET /v1/setup/license and accept the current wording. A browser tab open across an upgrade will hit this.
403The caller could not be identified.Acceptance records who agreed, so an unidentified caller cannot accept.

Side effects

  • Writes license_accepted_at, license_accepted_by, license_document_version, and license_document_sha256 to setup_state.
  • Writes a system.license.accept audit entry.

Proven by

  • services/nopsai/setup_license_test.go
  • services/nopsai/setup_license.go
  • services/nopsai/setup_wizard_status.go

How it works

Preflight and status answer different questions, and their access classes follow. Preflight asks "can this install be set up right now?" and is public, because a cold start has no working login. Status asks "has it already been set up?" and needs a token, because by then there is one to have.

Bootstrap errors are deliberately specific: an apply failure names the write or configuration reason, which is almost always a permissions problem on the mounted configuration volume rather than a bad payload.

Implementation evidence

  • services/nopsai/setup_preflight.go

    Preflight checks and the preflight-only server.

  • services/nopsai/setup_wizard_handlers.go

    Status, templates, and bootstrap handlers.