API changelog
Changes to the API, with the breaking ones and the deprecations.
What changed in the HTTP API, release by release, for people who write against it. The product's
own changes are in the CHANGELOG-*.md files; this one only says what a client can notice. The
promise it is held to, and how an operation is deprecated and retired, are in
api.md.
Each release has up to five lists (a major release puts its breaking changes first, so a client reads them before the rest):
- Added: new operations, parameters, fields and response codes. Always compatible.
- Changed, without breaking a client: behaviour that moved and that a client written to the documentation does not notice.
- Deprecated: an operation announced as going away, with its
Sunsetdate and what replaces it. It keeps working until then. - Removed: an operation retired after its
Sunset. - Breaking changes: anything a client written against the previous baseline could notice
that is not a removal after notice (a security fix, an operation that never worked). Each
entry says what, why and what a client does now. A change here also changes
panel/openapi.baseline.jsonin the same commit (python scripts/api_compat.py --update --accept-breaking).
4.0.0
API version 1: the first API that has one. This section is everything that differs from 3.3.1, which
is what panel/openapi.baseline.json holds. The API gains 617 operations (433 in 3.3.1, 1,050 now),
loses none, and python scripts/api_compat.py keeps passing. Work on it was first written down under
the number 3.4.0, which was never published; everything below ships in 4.0.0.
Breaking changes
What a client written against 3.3.1 can notice, most likely first. None removes an operation, a parameter or a field; each changes what a call answers or how it is addressed. Where a name from before 4.0 is still accepted, it is accepted until 5.0 and never written.
- The console's session cookies are
noust_sessionandnoust_csrf, and the header to echo isX-Noust-CSRF. They carried the product's previous name.GET /api/auth/sessionsays which, incsrf_cookieandcsrf_header, as it always did, so a client that follows it changes nothing. A script that names the cookies, or signs in with a jar it filled by hand, signs in again. The cookies of 3.x are never read, and a sign-in or a sign-out expires them. The header of before 4.0 is still accepted beside the new one, the WebSocket subprotocols of before 4.0 are accepted and answered in kind, and an API token with the old prefix works until it is revoked or expires; new tokens start withnoust_tok_. UPGRADING-4.0.md lists each old name and when it stops. GET /api/auth/sessionno longer answers itsrenamed_from_<previous name>field. The console used it to tell an operator, once, that the product had been renamed in 3.0; the notice is gone with the upgrade from 2.x, which 4.0 does not move. A client that read the field reads it as absent, which is whatfalsemeant.- An application's export is written as
"format": "noust-app"withnoust_version.GET /api/apps/{domain}/exportwrote the format name and the version key of the product's previous name.POST /api/apps/importreads both spellings, and the old version key stays in the schema for an export made before 4.0 (read, never written). A script that compares the format name, or reads the version key, accepts both. When a central copies an application to a server of 3.3.x, it writes the spelling that release reads. - A backup destination is tried before it is kept.
POST /api/backup-destinationsandPUT /api/backup-destinations/{name}connect with the fields given and answer400 destination_unreachable, with the storage tool's own words, when they do not; the destination is not saved (a change that does not connect leaves the destination as it was).verify: falsekeeps one that is down on purpose, to be tried later. - The administrator holds every permission unless the roles are split. On a server on the standard
profile (the default) the
adminrole now also holds the security officer's and the auditor's: accounts, security settings, compliance and the audit log.GET /api/auth/sessionlists more permissions for it than 3.3.1 did, and a route that answered403to an administrator now answers. The old matrix is back withsecurity.roles_split: true, or with a framework that asks for it (ENS medium and high, ISO/IEC 27001, SOC 2;security.profile: ens-mediumis ENS medium). In the same release a person's several accounts are no longer held to the separation of duties, and a new account is no longer held at the second-factor enrolment:mfa_requiredandmfa_pendingare true only where a framework asks for a second factor, or the console is published or listens beyond loopback. See COMPLIANCE.md and UPGRADING-4.0.md. - An update can be refused for a backup that did not happen. For an application with a database,
POST /api/jobs/updateand the calls that deploy fail the job before anything is pulled or built when the backup the update needs fails, instead of warning and going on (noust app backup-required DOMAIN offturns it off for one application). A Compose deploy or update is refused, with the holder named, when its stack publishes a port another application, a stream, a database or the console holds.
Added
Versions
- Every route is also served at
/api/v1/.... It is the same route as/api/...(same permission, same audit entry), not a copy;/api/...keeps working. X-Noust-API-Version: 1on every response under/api.GET /api/openapi.json:info.versionis the API's version (1; it was the product's version, andunversionedin the checked-in file),info.x-noust-versionthe product's, andinfo.x-noust-api-prefixthe versioned address.- Deprecated operations announce themselves with
Deprecation,SunsetandLinkheaders anddeprecated: trueplusx-noust-deprecationin the schema. None is deprecated yet.
Applications, builds and deployments
- The applications' side of the release:
GET /api/apps/{domain}/env/file(apps.read) is the.envas the text of a file, to view or to download (download=true); values are masked unlessunmask=true, which needs an admin credential and sudo mode and is audited.POST /api/apps/{domain}/env/sync(apps.manage) brings the text of a.envinto the variables (prune,audience,dry_run) and answers with names only.DELETE /api/apps/{domain}takesremove_volumesandremove_databases(both off unless asked), next toremove_filesandremove_ssl.GET /api/apps/{domain}/deletion(apps.read) lists what a deletion would touch: the directory, whether the stack has Docker volumes, and each database with whether it would be dropped and who else uses it.- A container application takes
commandandentrypoint(a list of words,nullfor the image's own,[]to clear it), andPOST /api/apps/{domain}/container/check(apps.deploy) says whether its registry image has a newer digest. - The webhook of an image from a registry:
GET /api/apps/{domain}/webhook/registry(apps.read) andPOST .../webhook/registry/reveal(secrets.reveal, audited) give the addresses for Docker Hub and GHCR;POST /hooks/registry/{domain}(GHCR'sregistry_package, signed with the application's webhook secret) andPOST /hooks/registry/{domain}/{token}(Docker Hub, which signs nothing, so the address carries a token derived from the secret) queue an update when a push moved the tag the application runs. GET /api/apps/{domain}/compose/document(secrets.reveal),POST .../compose/document/check(apps.manage) andPUT .../compose/document(apps.manage, sudo mode) read, judge and keep the Compose document of a stack deployed from one;editableandreasonsay when a stack has none.
PATCH /api/apps/{domain}/envtakesaudience(a name toboth,buildorruntime) for the variables whose audience changes, asPUTdoes; a name that is not in the file after the merge, or an audience that is not one of the three, is refused asaudiencebefore anything is written. Changing only the audience of a variable names it inchangedand sends no value.POST /api/apps/{domain}/deployments/{deployment_id}/rebuildtakesno_cache(off by default): the commit is built from scratch, with an empty package manager cache and no release on disk reused. A request with no body is unchanged.POST /api/appstakesteam,projectandenvironment(where the application goes; omitted, the team the caller works in, its default project andproduction),compose_document(a Compose document typed in, looked at first byPOST /api/compose/analyze, which judges a document without running or saving it),container(a container application: a Dockerfile, Railpack or a registry image,container.image, which then needs no source) and, with a recipe,recipe_database,default_backupsandinstall_requirements(install the runtime and the engines the recipe needs that the server lacks, which needs sudo mode and the server's permission).GET /api/appstakesteam,project,environmentandtagfilters, and an application answersteam,project,environmentandtags.- The deploy queue (releases.md):
GET /api/jobs/queue(apps.read) says what is building and what waits, in order, and the limits (deploy.max_concurrent_builds,deploy.queue_limit). One application builds one thing at a time, an update that is already waiting absorbs the same request, and one the queue has no room for is429. A job answersqueue_position,waiting_for,cancellable,cancel_requestedandcancel_blocked_reason;POST /api/jobs/{job_id}/cancelanswers the job'sstate, and a deployment that was cancelled has the statuscancelledwithcancelled,cancelled_atandcancelled_by.POST /api/jobs/updatetakesno_cachebesidefrozen_ok. POST /api/deploy(apps.deploy) deploys several applications in one call. Each is a job of its own, and one that has nothing new, is not found or meets a full queue is a result of its own that does not stop the others (ci.md).GET /api/deployments/{id}answerscertificate_stateandcertificate_output: a certificate the authority refuses leaves a deployment that succeeded, served over HTTP, with what the authority said kept on it.GET,PUTandDELETE /api/apps/{domain}/hookscarrypreview_post_create(run in order once a preview's first deployment serves) andpreview_pre_destroy(run before a preview is removed), as well as the hooks of the application itself (previews.md).- Uploads and pushes as sources (sources.md):
POST /api/uploads,PUTandGET /api/uploads/{upload_id}(a chunk at the wrong offset is409with the offset to resume from),POST .../completeandDELETEtake an archive up and extract it;POST /api/uploads/dockerfilemakes a project out of a Dockerfile that was pasted.GET /api/pushandPOST /api/push/setup(sudo mode,server.host_access) say whether the server takesgit pushand prepare it. - A JavaScript monorepo (workspaces.md):
POST /api/workspaces/inspectreads a repository's workspaces and says which are applications, andPOST /api/workspaces/deploy(sudo mode) queues one application per workspace named. - Code hosts (sources.md):
GET /api/deploy-keyslists the deploy keys and the applications each reaches,GET /api/deploy-keys/serverthe public half of the server's own SSH key (what to add at the code host before an application exists), andGET,POST/api/deploy-keys/hostsandDELETE /api/deploy-keys/hosts/{host}the host keys pinned (sudo mode andserver.host_accessto pin or forget one)./api/integrations/gitlaband itsconnections,connections/{name}/test,.../groups,.../projects,.../projects/{project}/branches,oauth-appsandoauth/authorize|callbackconnect GitLab with a token or by signing in;PUT /api/integrations/github/hostchooses the GitHub the App talks to (github.com, Enterprise Server or Cloud), andGET /api/integrations/githubanswershost. - Registry accounts for private images:
GET,POST /api/registries,GET,PATCH,DELETE /api/registries/{name}andPOST .../{name}/test(apps.read,apps.manage). The password is written, never read. - The export document (delivery.md) carries
mounts(a file's text only withwith_secrets),container(the settings of a container application, never its registry password or build-secret values), and, asGET /api/apps/{domain}/exportandPOST /api/apps/importnow keep them,delivery,source_optionsandapp.build, which the endpoints used to drop. A copy to another server through the central carries all of them. POST /api/compose/importtakesformat(coolify, the default, ordokploy) and, for a Dokploy template,template(itstemplate.toml); the answer gainsfiles(the files the template mounts, by path beside the document) andsecret_files(the ones that hold a generated secret). A request that sends neither is unchanged.POST /api/import/applicationsandPOST /api/import/proposalread CapRover too (platform: caprover; itstokenis the dashboard's password, used to sign in for the request and not kept). A proposal gainsstorage(the data the application kept outside its image, with the commands that copy it) anddatabase_details(the databases it used, with the commands that dump and load them); a value that looks like a credential, such as a connection string with a password in it, is withheld fromenvwhatever its variable is called.- The runtime of an application (runtime.md):
GET /api/apps/{domain}/processes,PUTandDELETE /api/apps/{domain}/processes/{name},POST .../processes/{name}/scaleandGET .../processes/{name}/logs(the processes that run beside the web process);GETandPUT /api/apps/{domain}/replicas(copies of the web process) andGETandPUT /api/apps/{domain}/hardening(the systemd sandbox of the unit): eachPUTneeds sudo mode and answers202with a job, because it restarts the application behind the health gate;GET,PUTandDELETE /api/apps/{domain}/mountsandGET .../mounts/content(files kept for the application, which needssecrets.reveal);GET /api/apps/{domain}/pending(changes not applied yet, never the values of secrets). GETandPUT /api/apps/{domain}/tuning: the limits beyond the hard ones (a soft memory limit, swap, CPU and disk weights, pinned CPUs, ulimits), the capabilities the process keeps and the devices it may open (runtime.md).PUTcarries the whole tuning, needs sudo mode and answers202with a job, because it restarts the application behind the health gate.GET /api/apps/{domain}/compose/variables: the variables a Compose stack's file asks for and whether the stack has each (set,default,unsetormissing), never a value (compose.md). A deployment or an update of a stack refuses before building when the file requires a value nothing gives.GET,PUTandPATCH /api/apps/{domain}/envcarrycomments(the note kept above a variable in the.env;PUTandPATCHtake them over the existing ones, an empty text takes one away), and a variable's value may have several lines when it holds no single quote and no control character but the line feed (variables.md).GET /api/apps/{domain}/replication: whether an application could run on more than one server, as the server it is on can tell, with what blocks it and what only warns (runtime.md).GET /api/apps/{domain}/disk,PUT /api/apps/{domain}/diskandPOST /api/apps/{domain}/disk/measure: what an application takes of the disk by place, the limit its deploys are held to, and whether its filesystem could hold a kernel quota; measuring is a job of typedisk(runtime.md).PUT /api/apps/{domain}/disk/kernel(sudo mode,apps.manage): gives the application's limit to the kernel as a project quota on XFS or ext4 mounted withprjquota, or takes it off; a job of typedisk.GET /api/apps/{domain}/diskanswerskernel(applied,project,mount,hard_mb,used_bytes), read from the kernel when the quota is applied.GET /api/apps/native-sites,POST /api/apps/adopt-siteandGET /api/apps/{domain}/adoption: list the sites on the server that Noust did not deploy, register one as it is (sudo mode), and say what an adopted application is (adopting.md). An adopted application has the typeadopted; an update, a rollback, a rebuild, an environment change or a deployer-backed operation on it answers 400 with the reason, and deleting it removes nothing it is made of.GET /api/cron/{name}/ledger: which moments a scheduled task was due and whether each ran on time, late or not at all (runtime.md). The timeline'sjobssource carries the ones that did not ascron_missedevents.- The files of an application, as its own account (all need
apps.terminal; reading a file and every change need sudo mode):GETandDELETE /api/apps/{domain}/files,GETandPUT .../files/content,GET .../files/download,PUT .../files/upload,POST .../files/directoryandPOST .../files/move. GETandPUT /api/config/delivery:max_minutes, the longest a build may take (60 by default; 0 is no limit), besidemax_concurrent_buildsandqueue_limit. A build past it fails with the time limit as the reason; its job and deployment arefailed, notcancelled.GET /api/apps/{domain}/healthreads the health check with the defaults marked, andPATCH /api/apps/{domain}/health(and the response) gainkind,command,service,method,scheme,host,port,interval,retries,start_periodandeffective_summary.GET /api/deploymentsandGET /api/deployments/{id}:rollback_method(instant,rebuildorsnapshot) says how going back to the deployment is done.GET /api/deployments/{id}/logtakeslevel,search,timestampsandsanitize.POST /api/cronand the jobs it lists:timeout(seconds, 1 to 86400) andservice(the service of the application's Compose stack the command runs in).- Shared variables by scope (variables.md):
GET,POSTand per-namePUT,DELETEandusageunder/api/teams/{team}/variables,/api/teams/{team}/projects/{project}/variables,.../environments/{environment}/variablesand/api/variables/server. A variable hasvalue,secretandaudience(both,buildorruntime); a value may hold{{team.NAME}},{{project.NAME}},{{environment.NAME}},{{server.NAME}},{{app.URL}}or{{secret:PROVIDER/PATH}}. - Secret managers:
GET /api/secret-providers,GET /api/secret-providers/kinds,PUTandDELETE /api/secret-providers/{name}andPOST .../{name}/test. A token is written, never read. GET /api/apps/{domain}/envanswersaudience,referencesandpredefinedbesidesvariables;PUTtakesaudience;POST /api/apps/{domain}/env/resolveresolves the references again without a deploy.- Previews:
POST /api/apps/{domain}/previews(a preview by hand from apull_request, abranchor atag),GET .../previews/pull-requests,GET .../previews/{number}and.../logs,POST .../{number}/redeploy,/stopand/start, and the variables defined for previews under.../previews/variables. The settings take and answerinherit_env,allow_envanddata_mode;GET .../previewsanswershas_database, and a previewkind,refanddatabases. A Docker Compose application has previews. GET /api/apps/{domain}/previews/dns: whether the names of an application's previews reach this server, and the record to add when they do not.- Previews: the settings take and answer
domain_template,noindexandfork_policy, anddata_modemay beclone; a preview may have the statusawaiting(a fork's commit that waits for an operator), andPOST .../previews/{number}/approveand.../rejectdecide it. - Environments:
GET /api/apps/{domain}/promotionandPOST .../promoteput a release on the same application in another environment of its project, without building it again;POST /api/apps/{domain}/clonemakes another application with this one's settings. - Tags on applications:
GETandPUT /api/apps/{domain}/tags,GET /api/tags,POST /api/tags/{tag}/deploy, and atagsfield and atagfilter on the applications list. - Holding, freezing and scheduling deploys (delivery.md):
GETandPUT /api/apps/{domain}/rollout(the deploy history kept, the stop policy, whether a rollback pauses),POST .../rollout/pauseand.../rollout/resume;GETandPOST /api/deploy-freezesandDELETE /api/deploy-freezes/{id};GET,PUTandDELETE /api/apps/{domain}/update-schedule;POST .../releases/stage(build without serving) andPOST .../releases/{id}/promote(make it serve), both jobs. A release listswaiting. A deploy during a freeze, or by a pipeline during a pause, answers409with the codedeploys_frozenordeploys_paused;UpdateRequestand the deploy options takefrozen_ok. POST /api/jobs/update-manytakestags: every application that carries any of them, narrowed byteam,projectorenvironmentwhen those are given too.POST /api/tags/{tag}/deployis now this same call with one tag, so it also skips what the caller may not deploy where it lives (it queued it before).POST /api/manifest/apply(sudo mode,apps.manage): applies a manifest as a job made of the caller's own API calls, refused with409 plan_changedwhen theexpectdigest of the preview is not the plan's now, and with409 apply_runningwhile another is being applied. The preview (POST /api/manifest/plan) answers the plan'sdigest. The job's type ismanifest; it runs beside the deploy queue instead of in it.GET /api/map(apps.read) reads how the server fits together: the names that reach each application, the applications with their state, the databases, the scheduled work and the data they keep. It is computed on every call and carries no coordinates.GET /api/environmentsandGET /api/projectslist every environment and project the principal sees, whatever the team.GET /api/domains/temporary(apps.read) works out the temporary name of this server, or of an application withprefix,sourceorimage(shop.203-0-113-10.sslip.io), which resolves to the server's public address so that a certificate can be ordered. On a server with no public address it answers200withavailable: false, areasonand ahint; an available answer is as it was.
Recipes
- The catalogue (recipes.md):
GET /api/recipestakesq,category,kind,originandtag, and answerstotalandproblems;GET /api/recipes/{name}answers the recipe in full (its version, images, variables,public_ports,database_choices,build,license,changelog).GET /api/recipes/categories,GET /api/recipes/{name}/logoandGET /api/recipes/{name}/requirements(what the recipe needs from this server and whether it has it, changing nothing) read it;POST /api/recipes/{name}/install(apps.manage) queues the install, and a required variable nobody gave is a400naming every missing one before any job exists. - Installed recipes:
GET /api/recipes/installed,GET /api/recipes/installed/{domain}andPOST /api/recipes/installed/{domain}/update(a job that goes through the one update, with its backup, health gate and way back). - The server's own recipes and signed packages (
server.manage; what changes needs sudo mode):POST /api/recipes/privateand.../check,DELETE /api/recipes/private/{name},GET /api/recipes/catalog,POST /api/recipes/catalog/fetchandDELETE /api/recipes/catalog.
Databases
/api/databases/instances: the engines Noust runs itself, in containers, beside the host's (another version of an engine, or one the host does not provide): list,catalog,plan, make (a job, sudo mode), describe,start(a job),stop,upgrade(a job, sudo mode),remove-planand remove (confirm, a job, sudo mode). An instance is addressed everywhere an{engine}is taken by its key (postgresql@pg16).GET /api/databases/engines:managed,pinned_versionandmemory_limit_mbon an engine that is an instance Noust runs.GET /api/databases/provisioning/choices: each flavour lists itsinstancesand theinstance_versionsone could be made in; a version an instance runs hasstateinstanceand theinstancethat runs it.GET .../provisioning/plantakesinstance, and answers with it.POST /api/apps/{domain}/databasesandPOST /api/apps(database):instance, the name of an instance to make the database on. Aversionthe host does not run is answered by the instance that runs it, when exactly one does.- Point-in-time recovery of the host's PostgreSQL (databases.md):
GET /api/databases/pitr(the window that can be recovered, the base backups, the archiver, the timer, warnings),POST .../pitr/enable/planand.../pitr/restore/plan(read-only), and, as jobs with sudo mode,POST .../pitr/enable,.../disable,.../base,.../sync,.../fetchand.../restore. A change that restarts the engine or replaces the cluster is refused withoutconfirm. The reads needdatabases.read, the jobsdatabases.manage. - Importing (databases.md):
POST /api/databases/imports/inspectsays what a dump is and whether an engine can load it;POST /api/databases/importsloads it,POST /api/databases/imports/cluster(and.../cluster/plan) loads a dump of every database, with their accounts when asked; the dump comes up in chunks that resume (POST|GET|PUT|DELETE /api/databases/imports/uploads...). They needdatabases.manage, and loading needs sudo mode. A plain dump is loaded as the database's owner, never as the superuser, unless the request asks. POST /api/databases/moves(and.../plan) moves a database to another engine, with the way back;POST /api/fleet/databases/transfer(fleet.manage) moves one to another server of the fleet, andGET /api/databases/backups/{name}/datareads a range of a dump so that a central can carry it (sudo mode; the read is audited where it begins).- Reachable from the network:
GET /api/databases/public,GET /api/databases/public/{engine}/{database},POST .../public(and.../public/plan; withoutconfirmthe answer is a400 confirmation_requiredcarrying every warning),.../reveal(audited as a password shown) andDELETE. - Replicas and the TLS of the host's engines:
GET /api/databases/replicas[/{engine}],POST .../replicas/{engine}(and/plan) prepares a primary and answers the join code once,POST .../replicas/join(and/plan) makes this server a replica,.../promote,.../{name}/tokenandDELETE .../replicas/{engine}/{name}; they needdatabases.manageand sudo mode, and the plans change nothing. - Extensions and tags:
GET,POST /api/databases/databases/{engine}/{name}/extensionsandDELETE .../extensions/{extension};GET,PUT .../tagson a database and on a service (/api/services/{name}/tags;GET /api/servicestakes atagfilter and a service answerstags).GET /api/databases/databases,POSTand the database itself carryteam,project,environmentandtags, and the list takes the same filters. A link to a database takestlsand the detected links answertls_mode. POST /api/apps/{domain}/databases(and thedatabaseofPOST /api/apps) takesinstall, which installs the engine in the version asked for when the host lacks it, andGET /api/databases/engines/cataloganswerspackage_manager, the one of this host.
Backups and recovery
- Data and volumes (disaster-recovery.md):
/api/volume-backupslists, takes (POST), reads, verifies (POST .../{backup_id}/verify), pushes to a destination (.../push), restores (.../restore, sudo mode; the application is stopped, a safety copy taken, and the data put back if the health gate fails) and deletes data backups;.../policiesand.../policies/{domain}hold an application's policy andPOST .../policies/{domain}/runruns it;.../targets/{domain}lists the data an application declares,.../remote/{domain}what a destination holds, andPOST .../restore-remoterestores from there. - Noust's own state (disaster-recovery.md):
/api/self-backuplists the bundles and the schedule,POST /api/self-backupand.../runtake one,PUTandDELETE .../scheduleset the schedule, andPOST .../{name}/verifyand.../{name}/pushprove a bundle and send it to a destination,DELETE .../{name}removes it. - Encryption:
GET /api/backups/encryption(is it on, with which key, how many are sealed),POST .../enable,.../disable,.../rotate,.../keys(put a lost key back),GET .../keys/{key_id}(secrets.reveal),GETandPOST .../seal-existing(count, then seal what was made in clear), andPOST /api/backup-destinations/protect, which seals the credentials that destinations still keep in clear.POST /api/backup-destinationsandPUT .../{name}takeverify. - Failures:
GET /api/backups/failures,GET .../failures/{failure_id}and.../outputkeep and answer everything the tool said, plain. - A whole server:
GET /api/server/restore/planandPOST /api/server/restore(sudo mode) bring a server back application by application,GET /api/server/migration/plan,POST .../migration/prepareandGET .../migration/checkprepare a move to another server and check it once it has happened. - Schedules and policies:
POSTandPUT /api/backup-schedulestakemax_runtime_seconds,remote_only,retention_gbandtimezone; a database's policy (/api/databases/backup-policies/{engine}/{database}) takes the same andexclude_databases,exclude_schemasandexclude_tables, and answersexclusions,scope,remote_onlyandretention_gb.
Domains, certificates and the web server
PUT /api/apps/{domain}/domains/{name}/enabled({"enabled": false}) switches a name off without deleting it, or serves it again;enabledin every entry ofGET|POST /api/apps/{domain}/domains.force_httpsin the policy of an application (GET|PUT /api/apps/{domain}/policy).- The DNS check's
cdnis alsofastly,bunnycdnorarvancloud. - A site's policy (network.md):
GET,PUTandDELETE /api/apps/{domain}/policy,PUTandDELETE /api/apps/{domain}/policy/users/{username}, and the server'sGET /api/policy/cloudflare-rangeswithPOST .../refresh. A list that would refuse the caller's own address is a400onallow_ipsuntilaccept_lockoutis sent. - Domains:
portonGET /api/apps/{domain}/domainsand on its entries,canonicalon the list and on every change,force,portanddnsin the body ofPOST /api/apps/{domain}/domains,dnsandcanonicalon every change,PUT /api/apps/{domain}/domains/canonicalandPUT /api/apps/{domain}/domains/{name}/port. A wildcard (*.example.com) is a valid alias. Removing a name queues the certificate to be ordered again without it (certificate_job_id). A name another site already answers on is a409witherror: domain_overlap.verdict,cdnandresolveron the DNS check (here,cdn,elsewhere,none,unknown). - DNS:
/api/dns/providers(list with the kinds, add andPATCH,DELETE,.../verify),/api/dns/records,POST /api/dns/sweepandGET,POST /api/apps/{domain}/dns. A record somebody else made at a name is a409witherror: record_conflictandhostname,record_id,currentandwanted; the same request withreplace_recordandreplace_currentreplaces exactly that record. - Certificates:
GET,PUT /api/certs/acmeandPOST /api/certs/acme/register(a job),GET,POST /api/certs/uploadedandDELETE /api/certs/uploaded/{name},GET,PUT /api/apps/{domain}/certificate.sourceandchallengeon the entries ofGET /api/certs, andauto_renewis false for an upload. GET,POST /api/streams,POST /api/streams/planandDELETE /api/streams/{name}: the TCP and UDP ports nginx publishes.409when nginx cannot (error: prerequisiteerror, with the fix inhint).GET,POST /api/tunnels,DELETE /api/tunnels/{name},POST /api/tunnels/{name}/routesandDELETE /api/tunnels/{name}/routes/{hostname}: Cloudflare Tunnels. A conflicting DNS record is an outcome of the route (dns.outcome: conflict), not an error./api/certs/ca: the server's own certificate authority for names that only resolve inside a private network.GET(the authority, the certificates it signed, whether this server trusts it),POST(make it; sudo mode,settings.manage) andDELETE(force; sudo mode);GET .../root?format=pem|der(the public certificate),GET .../trust(how to trust it on each kind of device, with the fingerprint),POST|DELETE .../trust-here(this server's own store; sudo mode),POST .../issuedandPOST .../issued/{name}/renew(apps.manage). A second authority is409, and a server without one answers404(not_found).PUT /api/apps/{domain}/certificatetakessource: "internal"(anddays): the server's own authority signs a certificate for every name the application answers on and it is served at once.- A site and what answers around it:
GET /api/sites/{domain}/config/versionsand.../{version}keep every version of a site's file with a diff, andPOST .../{version}/restoreputs one back (sudo mode, tested by the web server before it is installed);GET /api/sites/{domain}answersedited_outsideandpending_reload.GET,PUT,DELETE /api/unknown-hostschoose what answers a name no site serves (sudo mode).GET /api/private-networks,GET /api/policy/forward-auth-presets(oauth2-proxy, authentik and Authelia) andPOST /api/waf/install(ModSecurity and the Core Rule Set, from the distribution, sudo mode).POST /api/server/security/fail2ban/webmakes fail2ban watch nginx, andGET /api/server/security/fail2bananswersweb_availableandweb_jails. - A site belongs to a project and an environment like an application:
POST /api/sitestakesteam,projectandenvironment, andGET /api/sites/{domain}answers them. - A certificate for one name:
POST /api/apps/{domain}/domainstakescertificate(shared, the default, orown),challenge(http-01ordns-01) anddns_provider;PUT /api/apps/{domain}/domains/{name}/certificategives a name a certificate of its own or takes it back, andGET /api/certs/{domain}answerssource,challengeandserves(which names the certificate presents) (certificates.md).
Fleet
POST /api/fleet/placements/{domain}/rolloutfollows the application's build server: with one recorded it is planned and run as the shipment (.../ship) and the plan'sactionsaysplacement_ship;build_on_eachbuilds on every server instead, and a git tag, a registry image tag or a build server that is itself a member are rolled out as before, with the reason in the plan's notes. Its options gainbuild_on_each. An application without a build server is unchanged.POST /api/nodes/{node}/ssh/authorization(fleet.manage): the line a bastion's operator adds, asked for with the bastion the operator means to set, before setting it, asnoust node ssh NAME --bastion ... --showdoes. Setting a bastion opens the tunnel through it, which cannot work until the bastion lets the central in;GET .../sshgave the line only once the bastion was set.- A release built on one server and run on others (a central's build server):
POST /api/apps/{domain}/releases/bundle(a job) builds without activating and leaves the bundle in a spool;GET .../releases/bundles,GET .../releases/bundles/{bundle_id},GET .../releases/bundles/{bundle_id}/data?offset=&length=(raw bytes) andDELETE .../releases/bundles/{bundle_id}read and let go of it;POST .../releases/uploads,GET/PUT .../releases/uploads/{upload_id}(offsetquery),POST .../uploads/{upload_id}/completeandDELETEtake one in, with the digest declared up front;POST .../releases/receive(a job, held by a freeze of deploys,frozen_ok) activates it behind the health gate. All areapps.deploy. GET,PUTandDELETE /api/fleet/builders[/{domain}]: the server that builds an application for the others (fleet.readandfleet.manage), andPOST /api/fleet/placements/{domain}/ship(fleet.manage, planned and queued like a rollout) builds on it and runs that build on the placement's servers.- An application on several servers (CENTRAL.md):
GET /api/fleet/placements,GET,PUTandDELETE /api/fleet/placements/{domain}(the servers compared, and the record of where it runs),POST .../rolloutandPOST .../copy(fleet jobs, withplan: trueto see the plan first) andGET .../upstreams(the servers written for a load balancer). A rollout takescommit,tagorimage_tag. - How the central connects to a server:
GETandPUT /api/nodes/{node}/ssh(the wait for the connection and a bastion;PUTneeds sudo mode and is tried before it is kept) and the node's inventory,GETandPOST /api/nodes/{node}/inventory, to move a server to another central. POSTandDELETE /api/fleet/cloud/servers/{node}/link: link a server to its machine at a provider, or forget it (sudo mode).- Servers made at a cloud provider (
/api/fleet/cloud, CENTRAL.md):providers,accounts(add, replace the token, verify, remove), an account'scatalog, the cloud-init library (scripts,scripts/preview) andservers(list, create as a job, describe, delete,power-on). Tokens are in a request body once and in no response. - Servers behind NAT (
/api/fleet/link, CENTRAL.md): the status,invites(create, cancel) andaccept.GET /api/nodesandGET /api/nodes/{node}answerlinkfor a server that dials in, and itstunnelcarriestransport: "link". PUT /api/nodes/{node}/monitoring(pause or resume a server's monitoring), andmonitoringon a node: how long it has been silent and whether it is paused.POST /api/fleet/actions:noust_updatetakes the optiontarget_version, andindex_refresh("Refresh package lists") is an action. A plan's server step says "Noust X -> Y", and a skipped server's reason may beon_the_wayorindex_behind.GET /api/fleet/metrics: on a central, one metric of every server over the same window, from each server's own history (fleet.read).- Guided enrollment:
POST /api/fleet/enroll/host-keyasks an address for its ed25519 host key, to be confirmed before anything is sent, andPOST /api/fleet/enroll(fleet.manage, sudo mode) installs Noust if needed, authorizes this central and joins the server. The credential is used once and kept nowhere. A node answerslabels,link,monitoringandteam.
Access, teams and signing in
- Single sign-on through an OpenID Connect provider (sso.md):
GET /api/auth/oidc/providers,GET /api/auth/oidc/start,GET /api/auth/oidc/callbackandPOST /api/auth/oidc/complete(public, counted by the sign-in lockout), and the security officer's/api/auth/oidc/config,/api/auth/oidc/config/{provider}(PUT,DELETE),/api/auth/oidc/config/{provider}/testand/api/auth/oidc/identities.POST /api/auth/login/second-factoralso finishes the step a provider's answer opens, andauth_methodof a session may now beoidc. - Single sign-on through a SAML 2.0 provider (sso.md):
GET /api/auth/saml/start,POST /api/auth/saml/acs(a form,SAMLResponseandRelayState) andGET /api/auth/saml/metadata/{provider}(public; the first two are counted by the sign-in lockout), and the security officer's/api/auth/saml/config,/api/auth/saml/config/{provider}(PUT,DELETE) and/api/auth/saml/config/{provider}/test.GET /api/auth/oidc/providerslists them withprotocol: "saml", a button that starts at/api/auth/saml/start; the ticket is collected atPOST /api/auth/oidc/completelike any other, andauth_methodof a session may besaml. - Sign-in against a directory (sso.md):
POST /api/auth/ldap/login(public, counted by the sign-in lockout) and the security officer's/api/auth/ldap/directories,/api/auth/ldap/directories/{name}(PUT,DELETE) and/api/auth/ldap/directories/{name}/test.GET /api/auth/oidc/providerslists them withprotocol: "ldap"and gains theprotocolfield on every entry. - SCIM 2.0 provisioning (sso.md):
/api/scim/v2(ServiceProviderConfig,ResourceTypes,Schemas,Users,Groups), authenticated by the bearer token of a source, and the security officer's/api/auth/scim/sources,/api/auth/scim/sources/{name}(PUT,DELETE) and/api/auth/scim/sources/{name}/users. POST /api/auth/oidc/identitieslinks a person at a SAML provider or a directory as well, by the name of the source. Group mappings takebind,projectandenvironment(a role held at a project or an environment) besiderole,teamandteam_role.PATCH /api/auth/tokens/{token_id}changes where a token is accepted from (allowed_cidrs); adding addresses or lifting the limit needs sudo mode. A token gets403 api_disabledwhileweb.api_enabledisfalse.- Teams, projects and environments (teams.md):
/api/teams(list, create),/api/teams/{team}(read, rename, delete),.../members,.../access(a role given on the team, a project or an environment),.../projectsand.../projects/{project}/environments(create, rename, delete),.../environments/{environment}/variables[/{name}]with.../usage, and.../notifications/channels(a team's own channels, withPOST .../{name}/test).GET /api/teams/mine,PUT /api/teams/activeandGET /api/teams/rolessay which teams the caller belongs to, choose the one it works in and describe what a team role may do. A resource outside the caller's teams is a404, never a403. - Roles of your own:
GET,POST /api/access/roles,GET,PATCH,DELETE /api/access/roles/{role}andGET,POST /api/access/bindings,DELETE .../{binding_id}(a role at any scope, the whole server included).POST /api/auth/tokenstakesteam,projectandenvironmentand answersbound. - Accounts: an account answers and takes
email;POST /api/auth/invitationstakesemail,teamandteam_role, mails the link when the server has a mail channel (emailed,email_error), andPOST /api/auth/password-reset/requestand.../confirm(public) choose a new password with a mailed link and a code of the second factor.GET,PUT /api/auth/oidc/local-loginsay and change whether passwords and passkeys still sign accounts in (sudo mode; refused while no administrator could sign in). - Compliance (COMPLIANCE.md):
GET /api/compliance(the frameworks on, the controls they force),POST /api/compliance/plan(what a change would do, writing nothing),PUT /api/compliance(turning a framework off or loosening a control needs sudo mode, and a change with warnings is made only onceacknowledgeis true) andGET /api/compliance/check(framework by framework, with the clause of each finding). - A terminal (terminal.md):
GET /api/terminal/status,POST /api/server/terminal(a ticket for a root shell,server.terminal, sudo mode, off until an administrator turns it on),GET /api/terminal/sessions,DELETE .../sessions/{session_id}, and the recordings kept in the audit (GET /api/terminal/recordings[/{recording_id}],audit.read). - MCP (mcp.md):
POST /api/mcpanswers one MCP message or a batch under the caller's own permissions,GET /api/mcp/statussays whether it is on, where to connect and what it offers, andPUT /api/mcp/settings(security.manage, sudo mode) turns it on or off and chooses the groups of tools.GETandDELETE /api/mcpanswer that the server keeps no stream or session between calls. - The console on a name of its own (console.md):
GET /api/console/exposure,POST /api/console/exposure/preview,POST /api/console/exposure(a job; the checks run first and a refusal is the response) andDELETE /api/console/exposure(server.host_access, sudo mode).
Logs, metrics and notifications
latency_p50_ms,latency_p95_msandvisitorsin the totals and the points ofGET /api/apps/{domain}/traffic; the alert metricapp_latency_p95(unitms).- The event kinds
security_release,container_restart_loopandrecipe_outdated, and thewebhook_headerssetting of the generic webhook channel. - The log-shipping kind
newrelic(andregion). - Log shipping:
GET /api/log-drains,PUTandDELETE /api/log-drains/{name}andPOST /api/log-drains/{name}/test(settings.read,settings.manage). A token is only written: the destination reportshas_secret, never the secret. - Notification channels by name (notifications.md):
GET /api/notifications/channels,PUTandDELETE /api/notifications/channels/{name}(sudo mode) with a channel's own settings, events, route and quiet hours,GET /api/notifications/events,GET /api/notifications/deliveries,POST /api/notifications/deliveries/sweepandPOST /api/notifications/deliveries/{id}/retry.POST /api/config/notifications/{channel}/testtakes the name of any channel, and the channel typespushover,ntfy,gotifyandteamsexist besides the five of 3.3;mattermost,matrix,larkandpagerdutyare further types. GETandPUT /api/config/email-api: the provider the monitor's mail goes through instead of SMTP (resend,postmark,brevoorses). Credentials are write-only: the answer says whether one is stored.PUTneeds sudo mode.- Alert rules:
GET /api/alerts/rules,PUTandDELETE /api/alerts/rules/{name}, andPOST /api/alerts/rules/{name}/enableand/disable.POSTandDELETE /api/alerts/rules/{name}/acknowledgetake a firing alert (apps.operate) or hand it back, and a state carriesacknowledged_atandacknowledged_by. GET /api/apps/{domain}/site-logs: the end of a site's access or error log, masked.GET /api/logs/searchand, on a central,GET /api/fleet/logs: one search over the logs of the applications the caller sees (apps.read,fleet.read).GET,PUT,DELETE /api/logs/retention: how long the journal keeps them (server.read,server.manage; sudo mode to change). See logs.md.- A notification channel (
GET /api/notifications/channels,PUT .../{name}) carriesflood(at mostmaxmessages inminutesminutes, the rest arriving as one) andsummary(what is below a severity waits for a message once a day); a delivery carriesheld_for(quiet,floodorsummary). GET /api/probes,PUT,DELETE /api/probes/{name},POST /api/probes/{name}/checkandGET /api/probes/{name}/history: addresses the monitor asks from outside the application. The status page (GET,PUT /api/probes/status-page,POST /api/probes/status-page/write) and its publication on a name (POST /api/probes/status-page/publish,DELETE /api/probes/status-page/site, jobs). See probes.md.GET /api/apps/{domain}/traffic: an application's requests by the hour (by the day beyond two days), how they ended, bytes served, and its top pages, networks, status codes and referrers (apps.read).GET /api/apps/{domain}/metricsnow names therequestsanderrors_5xxseries of any application whose site has an access log, not only a static site's or a PHP application's.GET /api/metrics/prometheus: the newest value of every metric in the Prometheus text exposition format, or OpenMetrics whenAcceptasks for it (serverserver.read).GET /api/apps/{domain}/metricsanswerscontainersfor a Compose stack: each container and the metric names of its own CPU, memory, disk and network series.GET /api/apps/{domain}/metrics:seriesmay also namedisk_read,disk_write,net_rxandnet_tx, the series of an application's disk and network traffic;GET /api/metrics/queryserves them like the others.- The notification events
cron_missed,cleanup_success,cleanup_failed,probe_failedandprobe_recovered(GET /api/notifications/eventslists them). - The notification events
alert_firing,alert_resolved,backup_missing,cron_failed,cron_success,os_patchesandtoken_expiring, and the configuration keysmonitor.disk_*,monitor.cert_warning_days,monitor.backup_*,monitor.os_patches_check_hoursandmonitor.token_warning_days. GET /api/server/logs/searchandGET /api/server/logs/context(secrets.reveal) answer a query over the journal with fields,AND,OR,-, quotes and groups, and show what was logged around one entry (logs.md).GET /api/apps/{domain}/logstakesserviceand answersserviceandservices(one service of a Compose stack at a time);GET /api/deployments/{id}/logtakeslevel,search,timestampsandsanitize.
The server, updates and support
GET/PUT /api/server/storage/schedule,POST /api/server/storage/schedule/runandGET /api/server/storage/history: the policy that cleans a server's disk by itself, and the history of every cleanup.GET /api/system/version:security_update,security_versionandadvisory_url. A release newer than the installed one that fixes a vulnerability is told apart from an ordinary update.GET /api/system/health: aSecurity posturecheck amongchecks(backups that leave unencrypted, a central's keys not sealed, a security release that is out), each also a line ofwarnings.GET /api/system/update/autoandPUT /api/system/update/auto(server.manage, sudo mode) say and change when, how and whether Noust installs its own new releases by itself: a window on the server's clock, the days it opens on and the channel (a new major version is never installed by itself).GET /api/system/changeloglists what changed in each release the installation ships with (offline),GET /api/system/release-notesreads what updating to a version will change before updating,GET /api/system/doctorruns the checks ofnoust setup doctor, andGET /api/system/support-reportbuilds the report ofnoust support report(its journal excerpt only for a credential that may reveal secrets). All areserver.read.
Changed, without breaking a client
GET /eventsand/api/nodes/{node}/eventsanswer429("error": "rate_limited") to a credential that already holds sixteen event streams, and a stream ends after twelve hours (the browser'sEventSourcereconnects by itself).- The anonymous rate limit, the sign-in lockout and the webhook budget count an IPv6 client by its /64.
- Every response carries
X-Permitted-Cross-Domain-Policies: none, and a longerPermissions-Policy. - The text of
GET /api/apps/{domain}/logs,GET /api/services/{name}/logsand the log stream has credentials of a recognisable shape (private keys, tokens,password=assignments, URLs with a password) replaced by***. GET /api/auth/sessionand the login answers saysecond_factorandmfa_requiredper the control described under Breaking changes; the sign-in answers do not otherwise change.- A new application gets the defaults of 4.0 (zero-downtime deploys once its first deployment answered, a unit in systemd's sandbox, a stack given no port of its own unless one is asked for); one that exists keeps its way until its operator changes it.
GET /api/servicesfilters onnoust_only; the name it had before 3.0 stays as a parameter markeddeprecatedin the schema, read whennoust_onlyis absent (until 5.0).
Deprecated
Nothing is announced with a Sunset date. The old names listed under Breaking changes are accepted until
5.0 and never written; the one parameter of that kind that the schema marks deprecated is the old
spelling of noust_only.
Removed
Nothing.
3.3.1 and earlier
The API had no version and no stated promise; /api was the only address. Version 1 starts from
what 3.3.1 served.