api

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: AGPL-3.0 Imports: 96 Imported by: 0

Documentation

Overview

Package api is the single-binary server surface for Atlas: it embeds one engine.Processor behind an HTTP API and serves an embedded web UI, so a single self-contained binary can deploy BPMN models, run instances, and (as the UI grows) view them in a browser. See ADR-0011.

Respecting the single-writer invariant

The engine is a single-writer partition (invariant I3): exactly one goroutine may touch a partition's processor and state. HTTP handlers, by contrast, run concurrently. The Server bridges the two by owning a run loop goroutine that is the sole toucher of the processor; handlers submit closures to it via do and block for the result. No processor method is ever called from a handler goroutine directly.

Scope of the skeleton

This is the Milestone S skeleton (ROADMAP.md): deploy XML, create an instance, read stats, health, and a static UI shell. Deployments are durable via an on-disk sidecar store (ADR-0019) reloaded on startup, so diagrams, versions, and recovered instances survive a restart; the eventual event-sourced deployment path arrives with the Milestone 4 public API. One honest limitation remains: there is no streaming job-worker transport yet (that follows the gRPC job protocol, ADR-0007), so an instance parks at its service task — exactly the "waiting token" the live viewer shows. Such a parked job can be finished by hand over HTTP (POST /jobs/{key}/complete, the operator mirror of .../fail from the incident model), but that is a synchronous operator affordance, not the leased, at-least-once worker protocol.

Index

Constants

View Source
const (
	GrantActionShare      = "share"      // a member was added or their role changed
	GrantActionUnshare    = "unshare"    // a member was revoked
	GrantActionVisibility = "visibility" // visibility changed (From → To)
	GrantActionTransfer   = "transfer"   // ownership moved (From owner → To owner)
)

Grant-audit action kinds. Each names a mutation on the sharing scope, recorded on the handler that performs it. The set is closed and small; a later action (e.g. a denied attempt) can join without reshaping the record.

View Source
const (
	VisibilityPrivate = "private"
	VisibilityShared  = "shared"
)

Visibility values for a project scope. A private project is visible only to its owner (and admins); a shared project additionally grants each listed member their role. The set is intentionally small and open: an org/public value can join later without reshaping the field (ADR-0071).

View Source
const (
	ScopeRoleViewer = "viewer"
	ScopeRoleEditor = "editor"
	ScopeRoleOwner  = "owner"
)

Scope roles, lowest to highest privilege. viewer reads, editor reads+writes, owner additionally manages membership, visibility, transfer, and deletion. owner is implicit (it is the project's OwnerID), never stored in Members. The list mirrors ADR-0044's "roles are a list, only some values enforced now" discipline, so richer roles cost no reshaping.

View Source
const (
	PrincipalTypeUser  = "user"
	PrincipalTypeGroup = "group"
)

Member reference types. A scope member is either a single user or a whole group (ADR-0180); a group grant applies its role to every user in the group. The type field was reserved in ADR-0071 so groups slotted in with no migration.

View Source
const (
	// DefaultRetentionInterval is the default cadence of the history-retention sweep.
	// Exported so the CLI can show it as the default of --retention-interval rather
	// than restate a number that would drift from this one.
	DefaultRetentionInterval = time.Minute
	// DefaultRetentionBatch bounds how many finished instances one sweep tick evaluates,
	// so the scan never blocks the run loop (ADR-0115 / ADR-0085 no-full-scan rule).
	// Together the two bound the drain rate of a backlog: DefaultRetentionBatch per
	// DefaultRetentionInterval. Exported for the CLI, like DefaultRetentionInterval.
	DefaultRetentionBatch = 1000
)
View Source
const (
	SourceLocal = "local"

	// SourceOIDC marks an account an OpenID Connect provider vouches for
	// (ADR-0210). Its ExternalID is that provider's
	// subject, it carries no password hash, and the pair (source, external id) is
	// what a federated login resolves by.
	SourceOIDC = "oidc"
)

Identity sources. A user authenticates either against a locally stored password (SourceLocal) or, in a future enterprise build, against an external identity provider (OIDC/SAML/LDAP) that maps its subject onto User.ExternalID. Storing the source now — rather than assuming "local" everywhere — is what lets external identities coexist later without a migration (ADR-0044).

View Source
const (
	// RoleAdmin administers the instance: accounts, groups, credentials, secrets,
	// settings, backup and restore. It is the one role that is a superset — an admin
	// reaches every route, because every route a role names is one an admin may need
	// on the day the person who normally does it is unreachable.
	RoleAdmin = "admin"

	// RoleModeler authors: drafts, forms, decisions, documentation, projects and
	// applications — and deploys them. Deploying is code execution (risk R-09), which
	// is why it sits behind a role at all rather than behind being signed in.
	RoleModeler = "modeler"

	// RoleOperator runs what is deployed: start, cancel, terminate and repair
	// instances, work incidents, read runtime data.
	RoleOperator = "operator"

	// RoleUser works on tasks and reads what it is given. It is what a new account
	// gets, and on its own it reaches nothing that changes a definition or an
	// instance.
	RoleUser = "user"
)

Well-known roles. Roles are a free-form list on the user, not a single "admin" bool, so richer RBAC can grow here without reshaping the record (ADR-0044).

Four of them, and each route says which one it needs (ADR-0209). They are a list, not a lattice: an account carries several, and the question asked at the boundary is only "does this principal hold the role this route names". So a modeller who is also to start test instances holds `modeler` *and* `operator` — deliberately, because the alternative is a rank order in which every widening of one role silently widens the ones above it.

View Source
const (
	// HistoryScopeAll writes every settled job. It is what "how long does a mail
	// send take" needs, and it is the larger bill.
	HistoryScopeAll = "all"
	// HistoryScopeFailed writes only the failures — much less volume, and still the
	// question most often asked of a history.
	HistoryScopeFailed = "failed"
)

Scopes an operator can choose for what reaches clio.

View Source
const ExitNothingToServe = 78

ExitNothingToServe is that status, exported so `atlas worker` leaves exactly the one its supervisor parks on. 78 is sysexits.h's EX_CONFIG — "something was found in an unconfigured or misconfigured state" — which is the condition, and it is far from the statuses a panic or a signal produces.

View Source
const MaxDynamicClients = 16

MaxDynamicClients is how many self-registered clients this server keeps. Past it, registering evicts the oldest one nobody approved — see the eviction rule above for why it is not a refusal.

Sized for what it is: an installation has a handful of workers, not hundreds, and every one somebody actually approved is exempt from the cap's eviction anyway. An operator who needs more registers them by hand, which has no cap.

View Source
const RoleDeployAgent = "deploy-agent"

RoleDeployAgent marks the principal a deploy token resolves to: a peer Atlas publishing a bundle here (ADR-0129). It is deliberately not a user role — no account carries it, it cannot be assigned, and it grants nothing on its own. What it may reach is decided by deployAgentAllowed below.

Variables

View Source
var Version = "0.6.0-dev"

Version is the Atlas product version reported to the UI and the CLI. It is UI/display metadata only and unrelated to a deployment's process version.

It is a var, not a const, so a release build can stamp the tag into it with

go build -ldflags "-X github.com/pblumer/atlas/api.Version=0.6.0"

A plain checkout build keeps the "-dev" suffix; the exact commit is always available from the embedded VCS metadata (see buildInfo).

Functions

func ApplyPendingRestore

func ApplyPendingRestore(dataDir string) (bool, error)

ApplyPendingRestore applies a staged full-snapshot restore, if one is present and complete, into dataDir — replacing each staged top-level entry and dropping the materialized state so recovery rebuilds it from the restored WAL. It MUST run before the WAL and state stores are opened (the engine holds them under the single-writer invariant), i.e. at the very start of a boot. It returns whether it applied anything.

Each staged entry is moved (rename) into place and the staging directory removed only once every entry is in place. The marker is removed last with the staging, so a crash mid-apply re-runs on the next boot: an entry already moved is simply absent from the staging and skipped, so the apply is idempotent.

func DefaultOffloadedKinds added in v0.3.0

func DefaultOffloadedKinds() []string

DefaultOffloadedKinds are the Worker Types Atlas moves onto a worker of its own accord, and supervises a worker for. It is the opt-out half of ADR-0164: somebody trying Atlas gets the out-of-process architecture without configuring anything, because the engine starts the worker itself.

A kind belongs here when a supervised worker can actually serve it, which is true two ways. Most of these need **no credential** at all: what each of them needs is something a worker has by being a separate process — a CPU for a script, network reach for a scrape, neither for a CSV parse.

Mail is the other way, and the reason the set is not simply "the unmanaged kinds". Its endpoint and password live in the worker store rather than the environment (ADR-0036/0041), so for as long as a child only inherited the environment, moving mail here would have handed every mail task to a worker with no mailbox. The engine now writes that configuration into the child's environment at spawn (see superviseEnv), which is the operator setting the worker up, done by the program — so mail is served, and it is served by the process that should be waiting on an SMTP handshake. Every managed kind here must be one superviseEnv provisions; TestEveryDefaultOffloadedKindCanBeServedByItsWorker holds that.

Active Directory is the third way in, and the one that made the set worth re-deciding (ADR-0182). It is not managed — it holds no worker record, and an AD task authors its own server — but its bind password is a per-task *reference* that resolves out of the vault, which a supervised worker can read no more than it can read the worker store. So it is defaulted on the same condition mail is: superviseEnv hands the child exactly the references the deployed models name (adWorkerEnv). It belongs here because a directory write is the plainest case of work that should never sit on the engine's loop — a bind, a modify and a close against a domain controller somebody else operates — and because the credential is worth less to an attacker in a worker than in the engine (ADR-0166's own argument for offloading it at all).

BMC Remedy is the fourth way in, and it is mail's way exactly (ADR-0192). It is a managed kind whose AR System address and service account live in the worker store and the vault, so it was excluded for as long as the engine had no worker to hand them to — not as a judgement about the kind. ADR-0106's amendment built that worker and the handover (remedyWorkerEnv), which leaves no reason for a ticket create, three round trips to somebody else's ITSM host, to keep happening on the loop.

Jira is the fifth, and it is Remedy's way exactly (ADR-0218). A managed kind whose site address and Atlassian credential live in the worker store and the vault, excluded only for as long as there was nothing to hand them to; ADR-0201's follow-ups built the worker and jiraWorkerEnv built the handover. What decided it was not the argument but watching an operator look for the worker: a kind the engine serves itself appears in the Workers view only when something is wrong with it, so a working Jira worker is a row that is folded away as quiet, in a table whose whole subject is who is doing the work.

clio is the eighth, and it is Remedy's way exactly (ADR-0231's successor): a managed kind whose endpoint and token live in the connector store and the vault, excluded only for as long as there was nothing to hand them to. It has a worker now (worker.RunClioJob) and clioWorkerEnv builds the handover, which leaves no reason for three round trips to somebody else's event store to keep happening on the loop — least of all for the write, whose whole point is that it is durable somewhere else.

REST and LDIF are the sixth and seventh, and they are what turns the deprecation of ADR-0164 into its rule (ADR-0233). An HTTP call to somebody else's host is the *original* case for not running integrations on the engine's loop — it was left in-engine only because a REST task's auth secret is a vault reference a supervised worker cannot resolve, which is AD's problem and now has AD's answer (restWorkerEnv). LDIF needs no answer: it reads and writes a file, and a supervised worker is a child on the same host.

LDAP is the ninth, and it is AD's way exactly (ADR-0233, slice 3). The two kinds share a shape — a task authors its own directory URL and bind DN, and names its bind password and client certificate as vault *references* — so it was excluded for the same reason AD was and is included now on the same condition: ldapWorkerEnv hands the child exactly the references the deployed models name. A bind, a modify and a close against a directory somebody else operates is the same work AD's argument was about; that one kind speaks it through Microsoft's dialect and the other through the standard one changes nothing about where it belongs.

SOAP is the tenth, and it is REST's way exactly (ADR-0233, slice 4) — the same call in an envelope, over the same protocol, with the same one thing that cannot travel: the credential behind its authSecret. soapWorkerEnv is restWorkerEnv with a different job type, which is the sense in which this slice was already decided when REST's was.

SharePoint is the eleventh, and it is Jira's way exactly (ADR-0233, slice 5): a managed kind whose Graph endpoint and OAuth bundle live in the worker store and the vault, excluded only for as long as there was nothing to hand them to. It has a worker now (worker.RunSharePointJob) and sharepointWorkerEnv builds the handover, which leaves no reason for an item create against Microsoft's Graph — a token fetch and an HTTP round trip — to keep happening on the loop.

SCIM is the twelfth, and REST's way a third time (ADR-0233, slice 6): the same call with a provisioning vocabulary, the same one thing that cannot travel. scimWorkerEnv is the third caller of one collector rather than a third copy of it.

temis is the thirteenth and the last (ADR-0233, slice 7), and it is the one that did not copy the others: a central decision is a business rule task, and its completion carries a durable evaluation record rather than only variables. Moving it meant widening the engine-worker completion contract so a worker can report the evaluation it performed — which is why it went last.

With it the record's "owed a worker half" table is empty.

func DefaultSupervisedWorkerOnlyKinds added in v0.4.0

func DefaultSupervisedWorkerOnlyKinds() []string

DefaultSupervisedWorkerOnlyKinds are the worker-only Worker Types Atlas supervises by default (ADR-0172). Unlike the offloaded kinds these have no in-engine form at all, so --in-process-connectors cannot apply to them: the worker is the only way to run them. It starts with nothing to serve and parks (exitNothingToServe); the moment an operator adds a tenant in the Console, refreshSupervisedWorkers brings it up — no flag, no restart of Atlas. That is what makes the tenant a Console entry rather than a deployment change.

func IsOffloadableKind added in v0.4.0

func IsOffloadableKind(name string) bool

offloadableKindNames lists every kind that can be named, for the error above. IsOffloadableKind reports whether a Worker Type has in-process handlers at all, which is what makes it nameable in --offload-connectors: offloading is the removal of those handlers, so a kind that has none (entra, which only ever runs on a worker) is refused there rather than silently accepted. A caller that wants a worker for a kind asks this first, so it can supervise a worker-only kind without walking into that refusal (ADR-0164/0168).

func SeedStateFromCheckpoint added in v0.5.0

func SeedStateFromCheckpoint(dataDir string) (bool, error)

SeedStateFromCheckpoint gives a data directory with no state store its starting point, from the newest checkpoint that verifies. It reports whether it seeded one.

It exists because a compacted log no longer carries the prefix that would rebuild the store: those records were deleted, and they live only in the checkpoint (ADR-0131). Recovery can detect that gap and refuse — and does (ADR-0280) — but refusing is only the right answer when nothing can close it. Where a checkpoint can, the server should start.

This runs at startup, before the store is opened, because that is the only moment installing state files is possible: replacing the files under an open Pebble store is not. It is the same installation the whole-instance restore performs, called rather than copied, so the two cannot come to disagree about what a checkpoint restores to.

A directory that already has a state store is left alone. That store is the newer answer, and replacing it with a checkpoint would discard everything applied since.

func SetMaxFolderScanForTest added in v0.5.0

func SetMaxFolderScanForTest(n int) func()

SetMaxFolderScanForTest lowers the folder scan budget and returns a function that puts it back. It is exported for the api_test package, which drives the bound through the real routes rather than reaching into this one — the behaviour at the budget is what a person sees, so it is tested from outside.

Types

type BuildInfo

type BuildInfo struct {
	Version  string // product version (api.Version), stampable via -ldflags
	Revision string // git commit, "" if built outside a VCS checkout
	Time     string // commit time (RFC3339), "" if unknown
	Modified bool   // true if built from a dirty working tree
	Go       string // Go toolchain version
}

BuildInfo is the exported view of the running binary's version and embedded VCS build metadata, for callers outside this package — the `atlas version` command. The GET /api/v1/info endpoint reports the same values.

func Build

func Build() BuildInfo

Build returns the running binary's version together with its embedded VCS build metadata. It reuses the same cached source as the /info endpoint, so the CLI and the HTTP surface never disagree about what is running.

type LogBuffer

type LogBuffer struct {
	// contains filtered or unexported fields
}

LogBuffer is a bounded, concurrency-safe ring of the most recent process log lines. The command tees the standard logger into it (log.SetOutput(io.MultiWriter(os.Stderr, buf))) and hands it to the server via WithLogBuffer, so an operator can read recent server logs over the web — e.g. to check the script-worker startup lines — without shell access to the host.

It only retains the last max lines, so it is a diagnostic tail, not durable storage; it holds no secrets beyond whatever the process already logs.

func NewLogBuffer

func NewLogBuffer(max int) *LogBuffer

NewLogBuffer creates a LogBuffer retaining the last max lines (defaulting to 1000 when max <= 0).

func (*LogBuffer) Lines

func (b *LogBuffer) Lines() []string

Lines returns a copy of the buffered log lines, oldest first.

func (*LogBuffer) Write

func (b *LogBuffer) Write(p []byte) (int, error)

Write appends the (possibly multi-line) log output, keeping only the most recent max lines. It never errors and always reports the full length consumed, so it is safe as an io.Writer log sink behind io.MultiWriter.

type OIDCConfig added in v0.5.0

type OIDCConfig struct {
	// Issuer is the provider's issuer URL, exactly as it appears in the tokens it
	// signs. It is both where discovery starts and what every token is checked
	// against, so a mismatch is a refusal rather than a warning.
	Issuer string

	// ClientID identifies Atlas at the provider, and is the audience every ID token
	// must name.
	ClientID string

	// ClientSecret authenticates the token exchange for a confidential client. Empty
	// is allowed: PKCE covers the flow either way, and a provider that registered
	// Atlas as a public client issues no secret.
	ClientSecret string

	// Scopes is the space-separated scope list, or empty for oidcDefaultScopes.
	Scopes string

	// Name is what the button on the login screen says. Empty falls back to the
	// issuer's host, which is a worse label than an operator would write and a
	// better one than "OIDC".
	Name string
}

OIDCConfig is the identity provider an operator configured. An empty Issuer means there is none, which is the default.

type Option

type Option func(*Server)

Option configures a Server at construction. Options are applied in New before the run loop starts, so they set fields that are read-only afterwards.

func WithAuth

func WithAuth() Option

WithAuth turns on authentication enforcement: /api/v1 requires a valid session (except the login call, product info, and the OpenAPI doc), and managing users requires the admin role. Off by default, so an existing single-binary deployment is unaffected until an operator opts in (ADR-0044).

func WithCheckpointRetention added in v0.2.0

func WithCheckpointRetention(keep int) Option

WithCheckpointRetention sets how many published checkpoints are kept (ADR-0131). A checkpoint hard-links the SSTables it captured, so keeping every one would pin every file the store ever wrote; keeping a few leaves a fallback if the newest is corrupt. A non-positive value restores the default.

func WithCheckpoints added in v0.2.0

func WithCheckpoints(every time.Duration) Option

WithCheckpoints enables periodic recovery checkpoints at the given cadence (ADR-0131). Each one snapshots the applied state, so a restart replays only the log past it rather than from genesis — recovery time becomes a function of the cadence instead of the log's whole length.

It is purely additive: nothing is deleted, the WAL stays the source of truth, and a missing, failed, or corrupt checkpoint only makes the next recovery slower (invariant I2). A non-positive cadence leaves checkpointing off.

func WithCollabKeepaliveInterval

func WithCollabKeepaliveInterval(d time.Duration) Option

WithCollabKeepaliveInterval sets how often an idle collaboration SSE stream writes a keepalive comment, the mechanism that detects a half-open browser connection so its session participant is reaped (ADR-0140). A non-positive value restores the default (15s). Tests pass a short interval to exercise it.

func WithDynamicClientRegistration added in v0.5.0

func WithDynamicClientRegistration() Option

WithDynamicClientRegistration opens RFC 7591 self-registration.

Off by default, deliberately: see the note at the top of this file. Turning it on means anybody who can reach this port can create a client record and be shown to your people on a consent screen — under a name they chose.

func WithExternalURL added in v0.5.0

func WithExternalURL(origin string) Option

WithExternalURL states the origin under which this server is reachable from outside — "https://atlas.example.com" — for the absolute URLs the discovery documents and the WWW-Authenticate challenge have to carry.

It exists for the deployment behind a proxy, which was every deployment with a certificate until Atlas could terminate TLS itself (ADR-0191): the scheme such a request arrives with is http, and the origin derived from it would name a URL no client can use. Setting this once is the reliable answer. Leaving it unset falls back to what the request says — which is right for direct access, for a server serving its own certificate (externalBase reads r.TLS), and for tests.

func WithInboundBatchLimit

func WithInboundBatchLimit(n int) Option

WithInboundBatchLimit caps how many clio events one poll of a subscription reads and republishes (ADR-0075). A non-positive value restores the default. It bounds the burst a single poll hands the run loop; a large backlog then drains as bounded catch-up across ticks instead of one unbounded publish storm.

func WithInboundPollInterval

func WithInboundPollInterval(d time.Duration) Option

WithInboundPollInterval sets the clio inbound bridge's poll cadence (ADR-0075). A non-positive interval disables the bridge (useful in tests that drive it directly). The default is 2s.

func WithLimits added in v0.6.0

func WithLimits(l limits.Limits) Option

WithLimits sets the installation's resource budgets. Without it a server runs on limits.Default, which is what every ceiling in the API was before they had a name. There is deliberately no way to remove a budget: "off" is the state they exist to prevent, so an unset field is a default and not an absence.

func WithLogBuffer

func WithLogBuffer(b *LogBuffer) Option

WithLogBuffer wires the server's recent-log tail, exposed at GET /api/v1/logs, so an operator can read server logs from the web UI. The command builds the buffer, tees the standard logger into it, and passes it here.

func WithMCP added in v0.5.0

func WithMCP(h http.Handler) Option

WithMCP mounts an MCP transport at /mcp, behind this server's own access boundary. The handler is taken as an http.Handler rather than a concrete type so the dependency runs one way: the mcp package adapts this API, and this package need not know it exists.

Mounting it here is the whole point. The transport used to be registered on a mux beside this one, which meant withAuth never resolved a principal for it and --auth did not gate it — while the adapter attached a service credential of its own to the calls it made, so reaching the port was enough to drive the API. Passing the handler in makes that a decision this package owns rather than one the wiring in cmd can make differently (ADR-0196).

func WithMetricsQuery added in v0.5.0

func WithMetricsQuery(cfg promquery.Config) Option

WithMetricsQuery points Panorama's historical context at a Prometheus-compatible store (ADR-0189 P5b-ii). It is read-only and unrelated to the /metrics endpoint Atlas serves: this is where somebody else keeps what they scraped. An empty URL leaves the metrics half of every context answer reported as not-configured, which is then true of the server rather than a guess about the store.

func WithOIDC added in v0.5.0

func WithOIDC(cfg OIDCConfig) Option

WithOIDC configures an OpenID Connect provider people may sign in with (ADR-0210).

Off unless it is given: without it the routes are not mounted and the login screen offers nothing but the password form, which is the behaviour every installation has today.

func WithOffloadedConnectorKinds added in v0.3.0

func WithOffloadedConnectorKinds(kinds []string) Option

WithOffloadedConnectorKinds names the managed Worker Types this server must NOT serve itself, so their jobs park for an external worker instead (ADR-0168/0164).

This is the operative act of relocating a kind. The type-keyed pull refuses a job type an in-process handler is registered for — that refusal is what keeps one job from being worked twice — so a kind stays in the engine until its handler is turned off here. What is left is exactly what an unconfigured worker already does: the job parks on the activatable index until something takes it.

An unknown name is refused at startup rather than ignored. An operator who misspells a kind would otherwise believe they had relocated it while it kept running in the engine, which is the one outcome this flag exists to prevent.

func WithOpenSearchExportInterval added in v0.2.0

func WithOpenSearchExportInterval(d time.Duration) Option

WithOpenSearchExportInterval sets how often the exporter polls the log for newly durable records (ADR-0114). A non-positive value restores the default (5s). Tests pass a short interval to exercise the loop.

func WithOpenSearchExporter added in v0.2.0

func WithOpenSearchExporter(cfg opensearch.Config) Option

WithOpenSearchExporter enables the OpenSearch event exporter (ADR-0114): a WAL-tailing sink that mirrors the durable event log into an OpenSearch index so history stays searchable and can outlive engine-side retention. It is opt-in — a config with an empty URL leaves the exporter off. The endpoint, credentials, and index live in server config, never in a model.

func WithPlaygroundSessions added in v0.5.0

func WithPlaygroundSessions(ttl, sweep time.Duration) Option

WithPlaygroundSessions sets how long an untouched Playground sandbox is kept and how often the sweep looks for one to reclaim. Non-positive values restore the defaults. An operator with long-running exploratory sessions can raise the TTL; tests pass a short one to exercise the sweep.

func WithPublicFormsCORS added in v0.4.0

func WithPublicFormsCORS(origins []string) Option

WithPublicFormsCORS allows the given web origins to call the unauthenticated /public/forms endpoints cross-origin, so a process's start form can be embedded in an external site (a custom order widget, say) rather than only iframed from Atlas's own page (ADR-0186). Off by default — with no origins the public endpoints send no CORS headers and a cross-origin fetch is blocked by the browser, exactly as before. The sentinel "*" allows any origin. It opens only the cookieless public surface; the authenticated /api/v1 surface is never CORS-enabled, and no Access-Control-Allow-Credentials is ever sent, so a permissive origin still reaches only what a visitor to the public link can.

func WithRetention added in v0.2.0

func WithRetention(maxAge time.Duration) Option

WithRetention enables history retention (ADR-0115): a finished instance whose terminal event is older than maxAge and whose events are already exported (its terminal position is at or below the safe position) is hard-deleted from the state store. A non-positive maxAge leaves the server with no default age — the opt-in default — and retention then applies only to definitions declaring their own atlas:historyTtl (ADR-0144).

func WithRetentionBatch added in v0.2.0

func WithRetentionBatch(n int) Option

WithRetentionBatch caps how many finished instances one retention sweep tick evaluates and purges (ADR-0115), bounding the work a single tick does on the run loop; a larger backlog then drains as bounded catch-up across ticks. A non-positive value restores the default.

func WithRetentionInterval added in v0.2.0

func WithRetentionInterval(d time.Duration) Option

WithRetentionInterval sets the retention sweep cadence (ADR-0115). A non-positive value restores the default. Tests pass a short interval to exercise the sweep.

func WithSQLProbe added in v0.5.0

func WithSQLProbe(p SQLProbe) Option

WithSQLProbe gives this server a way to check a SQL worker's connection string (ADR-0220). Pass worker.ProbeSQL, which opens the product's driver and pings it.

It is what makes the Console's check work for the SQL kinds. Without it the check answers "this server cannot check a database connection", because that is the truth: nothing in this package can dial one.

func WithScriptWorker

func WithScriptWorker(jobType int32, exec script.Exec) Option

WithScriptWorker registers the interpreter for one script language (identified by its reserved job-type index, e.g. compiler.PwshJobTypeIndex), so a deployed script task in that language actually runs instead of parking on its job (ADR-0047). It executes arbitrary interpreter code on the server host, so register only the languages whose trust boundary is acceptable (the eventual isolation boundary is an external worker in the customer's environment). exec is the interpreter seam — pass script.New(script.Python) etc., or a fake in tests.

func WithSupervisedWorkers added in v0.3.0

func WithSupervisedWorkers(serverURL string, specs []SuperviseSpec, handles [][]string) Option

WithSupervisedWorkers asks the server to run these workers itself: one child process per entry, restarted while the server lives (ADR-0157 step 7).

It is an Option, so it comes from the process's own command line and from nowhere else. Nothing a request carries can add, change or name a supervised worker's command — the API can restart one that is already configured, and can do nothing else to it.

func WithSystemProcesses added in v0.2.0

func WithSystemProcesses() Option

WithSystemProcesses bootstrap-deploys Atlas's own embedded platform processes (user intake, access review, offboarding) into the protected system project at startup (ADR-0122). Opt-in — the installed binary (cmd/atlas) enables it, while the engine tests construct servers without it so a fresh instance still starts with no deployments.

func WithTargetTLSRoots added in v0.5.0

func WithTargetTLSRoots(pool *x509.CertPool) Option

pushBundle performs one target's outbound request. It never returns an error: every failure mode — unreachable, refused, malformed reply — is that target's reported outcome, because the caller is promoting to several independent servers and one being down says nothing about the others. WithTargetTLSRoots trusts an operator's certificate authorities, in addition to the host's, when this server calls another Atlas — publishing an application to a deployment target, and reading that target's status back.

validateTargetURL demands https of a peer, and a peer on-prem usually presents a certificate an internal CA issued, which the host's roots do not know. Without this the only answer is the host's trust store, and in a container that is an image change. It never replaces the system roots and it is never a way around verification — the skip-verify switch this file refuses stays refused — and it deliberately reaches no further than a peer Atlas: a worker calling a third party keeps the host's roots, because its endpoint is somebody else's (ADR-0129, ADR-0191).

func WithTracing added in v0.3.0

func WithTracing() Option

WithTracing wraps every /api/v1 route in an OpenTelemetry server span (ADR-0142). The command passes it when a collector endpoint is configured; without it the routes are registered bare, so tracing costs exactly nothing when nobody asked for it.

The engine is deliberately not traced — see the tracing package for why, and for the test that keeps it that way.

func WithUserProvisioning added in v0.2.0

func WithUserProvisioning() Option

WithUserProvisioning enables the in-process user-provisioning worker (create/set-password/disable Atlas logins) for the protected system project's processes (ADR-0123). Opt-in and off by default: it deliberately, and narrowly, reopens the ADR-0044/0049 boundary that no automated identity may manage users — so an instance keeps the human-in-the-loop ADR-0122 behavior until an operator turns this on. When off, a userConnector job has no worker and parks.

func WithWALCompaction added in v0.2.0

func WithWALCompaction() Option

WithWALCompaction deletes the WAL segments a recovery checkpoint and every consumer watermark make redundant (ADR-0131), bounding the log's disk instead of letting it grow with all history. It has no effect without WithCheckpoints: the cut is derived from a checkpoint, and compaction runs on the tick that takes one.

It is opt-in, unlike checkpointing, for the same reason history retention is (ADR-0115): this is the one step here that destroys data. The cut itself is conservative — the newest **fully verified** checkpoint (manifest and state files) at or below the store, floored by every consumer watermark, with a corrupt, foreign, or ahead-of-store checkpoint licensing no deletion at all — but a conservative cut is still a deletion, and an operator should choose it.

func WithWorkerHistory added in v0.4.0

func WithWorkerHistory(connector, scope string) Option

WithWorkerHistory sends every settled job run to a clio worker, so a worker's history outlives this process and its retention becomes the operator's own policy in their own store (see workerhistory.go).

Like the supervisor's configuration it is an Option, so the worker is named on this server's command line and nowhere else. The name is resolved at write time rather than at startup: an operator may create the worker after the server is running, and the history should start flowing when they do rather than at the next restart.

scope is HistoryScopeAll or HistoryScopeFailed; anything else means all.

func WithoutDocs

func WithoutDocs() Option

WithoutDocs disables the OpenAPI document at /api/v1/openapi.json and the Scalar API explorer at /api/docs, which are otherwise served by default. Pass it when the interactive, mutating "Try it out" surface should not be exposed (ADR-0043).

func WithoutMetrics added in v0.2.0

func WithoutMetrics() Option

WithoutMetrics turns off the Prometheus exposition at /metrics (ADR-0142). It is served by default — a system meant to be operated should be observable without extra configuration, and the exposition carries only bounded-cardinality aggregates — so this is for an operator who does not want the surface open at all.

func WithoutVault

func WithoutVault() Option

WithoutVault disables the engine-internal encrypted secret vault, which is otherwise on by default (ADR-0070). With it disabled the secret endpoints return 503 and worker credentials resolve only from the environment (ADR-0041 A2). Pass it when Atlas must not custody a key or ciphertext at all.

type ResetPasswordOptions

type ResetPasswordOptions struct {
	// DataDir is the server's data directory (the same --data-dir the server
	// runs with). The user store lives in its "users" subdirectory.
	DataDir string
	// Username identifies the account to reset, matched case-insensitively.
	Username string
	// NewPassword is the replacement password. It must meet the same minimum
	// length the API enforces.
	NewPassword string
	// CreateAdmin, when set, creates a fresh enabled admin with Username if no
	// such user exists yet — a bootstrap escape hatch for an instance that has
	// users but no reachable admin. It never duplicates or re-roles an account
	// that already exists.
	CreateAdmin bool
	// Now is the unix-seconds timestamp stamped into the record, matching the
	// server's own UpdatedAt/CreatedAt unit. Injected so the operation is
	// deterministic and testable.
	Now int64
}

ResetPasswordOptions configures a single operator-driven password reset.

type ResetPasswordResult

type ResetPasswordResult struct {
	UserID   string
	Username string
	// Created is true when a new admin account was created (CreateAdmin), false
	// when an existing account's password was replaced.
	Created bool
}

ResetPasswordResult reports what a reset did.

func ResetPassword

func ResetPassword(opts ResetPasswordOptions) (ResetPasswordResult, error)

ResetPassword sets a new local password for a user in the store under opts.DataDir. It replaces only the password (and UpdatedAt); roles, email, and the Disabled flag are left as they are, so a reset re-grants access without silently changing what the account can do. With CreateAdmin and no matching user, it instead creates a fresh enabled admin.

It touches the same files the running server uses. Login reads the store from disk on every attempt, so a reset takes effect on the next sign-in without a restart; running it against a live server is safe (writes are atomic), though stopping the server first is never wrong.

type SQLProbe added in v0.5.0

type SQLProbe func(ctx context.Context, product sqldb.Product, dsn string) error

SQLProbe opens one database and reports whether it answers. It is the seam that lets the Console check a database without the engine linking a database driver.

ADR-0173 keeps the drivers in `worker`, so `api` — which resolves SQL tasks and never executes them — links none of them. A check therefore cannot call sql.Open here; it is handed in by whoever assembles the binary, which for the single binary (ADR-0011) is the same process that runs workers. An embedder who wires nothing gets a check that says it cannot run, which is honest, rather than a worker reported broken because a driver was absent.

type Server

type Server struct {

	// SuperviseSpecs, superviseHandles and superviseURL are what to run, read off
	// the server's own command line and held in the same order.
	SuperviseSpecs []SuperviseSpec
	// contains filtered or unexported fields
}

Server hosts the engine behind an HTTP surface. Construct it with New, mount Handler on an http.Server, and call Close to stop the run loop.

func New

func New(proc *engine.Processor, store *state.Store, dataDir string, opts ...Option) (*Server, error)

New builds a Server over an already-recovered processor and its store and starts the run-loop goroutine. dataDir is the base data directory; the durable deployment and draft sidecar stores live in its "deployments" and "drafts" subdirectories (ADR-0019). New reloads any deployments found there, re-registering them with the processor so recovered instances resolve their definition and the UI can render diagrams again. The caller retains ownership of proc and store (Close here stops only the loop, not the engine).

func (*Server) Close

func (s *Server) Close()

Close stops the run-loop goroutine. It does not close the processor, log, or store — the caller owns those.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the HTTP handler: the JSON API under /api/v1, the probes, the MCP transport when one was supplied (WithMCP), the public share links, and the embedded web UI at the root — every one of them behind the access boundary.

With --docs (the default) it also serves the OpenAPI document at /api/v1/openapi.json and the Scalar API explorer at /api/docs.

func (*Server) InternalToken

func (s *Server) InternalToken() string

InternalToken returns the internal service token a process this server started itself authenticates with (ADR-0049) — the supervised workers, which are handed it at spawn. It is empty unless auth is enabled, and is never served over any endpoint; only the constructing process reads it.

type SuperviseSpec added in v0.3.0

type SuperviseSpec struct {
	ID    string
	Kinds []string
	// Workers are built-in Worker Types this worker serves (--connector on the
	// child). A worker may serve these, model-authored job types, or both.
	Connectors []string
}

SuperviseSpec is one supervised worker as configured: what to call it and which job types it serves. Both come from the server's command line.

type User

type User struct {
	ID           string   `json:"id"`
	Username     string   `json:"username"`
	Email        string   `json:"email,omitempty"`
	DisplayName  string   `json:"displayName,omitempty"`
	Roles        []string `json:"roles"`
	Disabled     bool     `json:"disabled,omitempty"`
	Source       string   `json:"source"`
	ExternalID   string   `json:"externalId,omitempty"`
	PasswordHash string   `json:"passwordHash,omitempty"`
	CreatedAt    int64    `json:"createdAt"`
	UpdatedAt    int64    `json:"updatedAt"`

	// RolesUpgradedAt is when this record's Roles were last written under the role
	// model (ADR-0209). Zero means the record predates it,
	// and its roles are therefore not a statement about anything except admin —
	// nothing else was enforced when they were written.
	//
	// The marker is on the record rather than instance-wide because it describes one
	// account and has to travel with it: a full snapshot carries users and settings
	// together, but a design-time backup carries neither, and an instance-wide flag
	// restored without the accounts it describes would silently skip the upgrade for
	// them. Set at creation from then on, so an account deliberately narrowed to
	// `user` is never re-widened on the next start (upgradeLegacyRoles).
	RolesUpgradedAt int64 `json:"rolesUpgradedAt,omitempty"`
}

User is a person (or, later, an external identity) known to this Atlas instance. It is operator/config data, not engine state: it never flows through the WAL or the processor, so it lives in a durable sidecar store like forms and projects (ADR-0019/0028/0044) and touches none of the six engine invariants.

The field set is deliberately chosen so the enterprise trajectory (SSO, RBAC, deactivation, later multi-tenancy) needs no breaking change:

  • ID is a stable, opaque, never-reused primary key, decoupled from Username and Email so either can change (or be reassigned by an external IdP) without rewriting references to the user.
  • Roles is a list (RBAC-ready), not a boolean flag.
  • Source + ExternalID are the hook for external identity providers.
  • Disabled deactivates a user for lockout/offboarding without destroying the record (and the audit trail it anchors).
  • PasswordHash is a bcrypt hash for local users and empty for external ones.

type WorkerRuntimeMode added in v0.5.0

type WorkerRuntimeMode string

WorkerRuntimeMode identifies the execution boundary of a Worker Type as defined by ADR-0208. It replaces compatibility details such as managedConnectorKind.workerOnly on the Worker-oriented API without changing the existing runtime paths.

const (
	// WorkerRuntimeModeAtlasEmbedded means the trusted implementation executes in the
	// Atlas process through the existing post-fsync job-worker path.
	WorkerRuntimeModeAtlasEmbedded WorkerRuntimeMode = "atlas-embedded"
	// WorkerRuntimeModeAtlasSupervised means Atlas executes the trusted implementation
	// in an atlas worker child process that it supervises.
	WorkerRuntimeModeAtlasSupervised WorkerRuntimeMode = "atlas-supervised"
	// WorkerRuntimeModeExternal means Worker Instances are operated independently and
	// consume Atlas's public worker/job API.
	WorkerRuntimeModeExternal WorkerRuntimeMode = "external"
)

type WorkerTypeDefinition added in v0.5.0

type WorkerTypeDefinition struct {
	ID           string            `json:"id"`
	WorkerTypeID string            `json:"workerTypeId"`
	Version      string            `json:"version,omitempty"`
	Title        string            `json:"title,omitempty"`
	Vendor       string            `json:"vendor,omitempty"`
	Origin       WorkerTypeOrigin  `json:"origin,omitempty"`
	RuntimeMode  WorkerRuntimeMode `json:"runtimeMode"`
	Placement    string            `json:"placement"`
}

WorkerTypeDefinition is the canonical Worker-oriented view over Atlas's capability catalog. ID remains the existing authoring identifier so model bindings do not move; WorkerTypeID is ADR-0208's globally namespaced identity. Built-in managed Worker Types additionally expose their package metadata. Placement remains install-specific and is deliberately independent from RuntimeMode.

type WorkerTypeOrigin added in v0.5.0

type WorkerTypeOrigin string

WorkerTypeOrigin identifies where a Worker Type definition comes from.

const (
	// WorkerTypeOriginBuiltIn means the Worker Type metadata ships with this Atlas
	// release and its trusted implementation is part of the Atlas binary.
	WorkerTypeOriginBuiltIn WorkerTypeOrigin = "built-in"
)

Source Files

Directories

Path Synopsis
Package collab implements the first slice of ADR-0140: live collaborative modeling sessions.
Package collab implements the first slice of ADR-0140: live collaborative modeling sessions.
Package formgen generates a form from a description and from the process it belongs to (ADR-0260).
Package formgen generates a form from a description and from the process it belongs to (ADR-0260).
Package httpapi holds the primitives every Atlas HTTP handler is written against: how a response is written, who the caller is, and where the request came from.
Package httpapi holds the primitives every Atlas HTTP handler is written against: how a response is written, who the caller is, and where the request came from.
Package infomodel holds Atlas's process information model: a UML class-diagram subset that gives BPMN's data objects a type they can share across processes (ADR-0230).
Package infomodel holds Atlas's process information model: a UML class-diagram subset that gives BPMN's data objects a type they can share across processes (ADR-0230).
Package layout generates BPMN diagram interchange (BPMN-DI) for models that carry none, and regenerates it for models whose layout a user has tangled.
Package layout generates BPMN diagram interchange (BPMN-DI) for models that carry none, and regenerates it for models whose layout a user has tangled.
Package panorama owns Atlas's design-time ArchiMate models (ADR-0189).
Package panorama owns Atlas's design-time ArchiMate models (ADR-0189).
Package playground serves the Modeler's Playground area: a caller opens a session on a model, feeds it cases, and drives it — free-running, or one occurrence at a time with a person answering the human tasks.
Package playground serves the Modeler's Playground area: a caller opens a session on a model, feeds it cases, and drives it — free-running, or one occurrence at a time with a person answering the human tasks.
Package processdoc serves process documentation (ADR-0143): a published BPMN process as a stored PDF plus the element prose it describes, its version history, and the revocable public link a reader without an account follows.
Package processdoc serves process documentation (ADR-0143): a published BPMN process as a stored PDF plus the element prose it describes, its version history, and the revocable public link a reader without an account follows.
Package runloop carries Atlas's single-writer boundary for design-time and API state.
Package runloop carries Atlas's single-writer boundary for design-time and API state.
Package sidecar is the durable-file discipline Atlas's design-time stores share.
Package sidecar is the durable-file discipline Atlas's design-time stores share.
Package taskfolder serves the Tasks app's folders: the saved filters a person builds for themselves out of listboxes, so a recurring question ("what is open on customer enquiries?") becomes a place in the sidebar instead of something retyped into the search box every morning (ADR-0268).
Package taskfolder serves the Tasks app's folders: the saved filters a person builds for themselves out of listboxes, so a recurring question ("what is open on customer enquiries?") becomes a place in the sidebar instead of something retyped into the search box every morning (ADR-0268).
Package token mints and validates Atlas's opaque share tokens.
Package token mints and validates Atlas's opaque share tokens.
Package vault is Atlas's engine-internal encrypted secret store: worker credentials sealed at rest with AES-256-GCM under a master key that never leaves the operator's control (ADR-0069), on by default with a generated key file when no operator key is supplied (ADR-0070).
Package vault is Atlas's engine-internal encrypted secret store: worker credentials sealed at rest with AES-256-GCM under a master key that never leaves the operator's control (ADR-0069), on by default with a generated key file when no operator key is supplied (ADR-0070).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL