Documentation
¶
Overview ¶
Package manifest is the deploy-time self-description surface for a nexus app. It produces the JSON returned at GET /__nexus/manifest — a single document an external orchestration platform reads to provision the app without per-app glue.
The contract is opt-in: an app boots fine with no manifest providers registered (the resulting manifest just has empty arrays for those sections). Adoption is incremental — modules implement the small interfaces below as they have something to declare.
Wiring (planned, see "Integration" at the bottom of this file):
- *nexus.App grows three registration methods: app.DeclareEnv(EnvProvider) app.DeclareService(ServiceDependency) app.UseVolume(Volume) plus a startup-task registration that collects via an fx group "nexus.startup-tasks" so any module can declare one without holding an *App reference.
- dashboard.Mount accepts a func() Manifest and exposes GET /__nexus/manifest as Handler() below.
- The framework's own infra modules (cache, ratelimit, future db) ship default EnvProvider implementations so apps that use them get accurate env-var declarations for free.
Index ¶
- Constants
- func AvailableEnvironments(m Manifest) []string
- func ComputeHash(m Manifest) string
- func Extract(raw []byte) ([]byte, error)
- func PrintJSON(w io.Writer, m Manifest) error
- type AdminPaths
- type AppIdentity
- type Autoscale
- type CORSBlock
- type CORSPatch
- type Check
- type CronSummary
- type DeployTOMLInputs
- type Deployment
- type EndpointSummary
- type Entity
- type EntityField
- type EnvProvider
- type EnvValidation
- type EnvVar
- type EnvVarPatch
- type Environment
- type EnvironmentTOML
- type ErrCode
- type ErrorsBlock
- type ErrorsPatch
- type File
- type FilePatch
- type Finding
- type Frontend
- type Health
- type Hooks
- type Inputs
- type Issue
- type Manifest
- type MergeError
- type Module
- type Override
- type Port
- type Range
- type Registrar
- type Route
- type Scaling
- type Secret
- type SecretPatch
- type ServiceDependencyProvider
- type ServiceNeed
- type ServicePatch
- type Severity
- type StartupTask
- type TLSBlock
- type TLSPatch
- type Volume
- type VolumeProvider
- type WorkerSummary
Constants ¶
const ( BeginMarker = "===BEGIN-NEXUS-MANIFEST===" EndMarker = "===END-NEXUS-MANIFEST===" )
BeginMarker / EndMarker bracket the JSON in print-mode output. Each is on its own line; Extract reads the bytes between (exclusive of the markers themselves). Picked for low collision probability with realistic log output — neither string is plausible as a log prefix or JSON content.
const EnvVarPrintAndExit = "NEXUS_PRINT_MANIFEST"
EnvVarPrintAndExit, when set to "1" in the app's environment, instructs nexus.Run to wire the fx graph far enough to collect every declared EnvVar / ServiceNeed / Volume / StartupTask, print the resulting Manifest as JSON to stdout, and exit 0 — without binding listeners or starting any background work.
This is the orchestration platform's intended path for getting the manifest: at upload / git-sync time, the platform builds the project's image, then runs `docker run --rm -e NEXUS_PRINT_MANIFEST=1 <image>` once. The container prints the manifest and exits, the platform stores it on the build row, and the deploy planner reads it offline. No HTTP exposure, no auth surface, no chicken-and-egg for sidecars/env that would otherwise be needed to boot.
Implementations of EnvProvider / ServiceDependencyProvider / VolumeProvider must keep their declarations side-effect-free — returning static metadata, not probing Redis or opening DB connections — because print mode walks them before the app's real lifecycle starts.
const SchemaV1 = "1"
SchemaV1 is the current manifest schema version emitted by Build. Consumers (orchestrators, CI, dashboards) gate on the major: a "1.x" manifest is guaranteed to be readable by any v1-aware consumer. Additive changes bump the minor (1.0 → 1.1); field removal or shape change bumps the major and is announced.
Variables ¶
This section is empty.
Functions ¶
func AvailableEnvironments ¶ added in v0.39.0
AvailableEnvironments returns the names of every declared environment in stable sorted order. Useful for CLI / lint output.
func ComputeHash ¶ added in v0.23.0
ComputeHash returns the canonical content hash of m, suitable for "did the manifest change between these two builds?" checks. The hash EXCLUDES ManifestHash itself (so it doesn't depend on its own value) and App.GeneratedAt (so re-emitting the same build produces the same hash). Returned in "sha256:<hex>" form to match common content-addressed schemes; downstream tools can string-compare without parsing.
Determinism rests on three things, all already true elsewhere in Build():
- struct fields are encoded in declaration order by encoding/json,
- all manifest slices are sorted before this is called,
- manifest maps (only EnvVar.ExposeAs etc.) are sorted by key by Go's encoder since 1.12.
If a future field stores a non-deterministic shape (random IDs, timestamps), exclude it here the same way GeneratedAt is.
func Extract ¶ added in v0.23.1
Extract pulls the manifest JSON bytes from a print-mode stream that may also contain stdout pollution (user stdlib log / zap / zerolog lines emitted during fx graph construction).
Three resolution paths, tried in order:
- Marker pair found → return bytes between (BEGIN+EOL stripped, EOL+END stripped). v1+ contract.
- Whole input is valid JSON → return it. v0 happy path: nexus binaries that emit raw JSON with no stdout pollution.
- Polluted v0 output → scan for embedded JSON objects via the stdlib decoder, return the LAST one containing "schemaVersion". Recovers manifests from binaries built against pre-marker nexus whose stdout also carries log emissions during fx construction. Without this, the v0 fallback would return raw bytes that fail to parse and the operator sees a generic JSON error instead of the actual manifest.
One-marker (only BEGIN, only END) is treated as truncation: error.
func PrintJSON ¶
PrintJSON writes the manifest as pretty-printed JSON to w. Used by nexus.Run when EnvVarPrintAndExit is set; callable directly by tests / tooling that wants the same output without the os.Exit.
Output is wrapped in BeginMarker / EndMarker sentinels on their own lines so parsers can extract the manifest from a stream that also carries unrelated stdout (a user's stdlib log / zap / zerolog emissions during fx graph construction). ExtractJSON walks any io.Reader and pulls out the bytes between the markers; absent markers, callers fall back to treating the whole stream as JSON (the v0 contract — preserved for binaries built against earlier nexus releases).
Types ¶
type AdminPaths ¶ added in v0.23.0
type AdminPaths struct {
ManifestPath string `json:"manifestPath,omitempty"`
HealthPath string `json:"healthPath,omitempty"`
ReadyPath string `json:"readyPath,omitempty"`
MetricsPath string `json:"metricsPath,omitempty"`
DrainPath string `json:"drainPath,omitempty"`
ReloadPath string `json:"reloadPath,omitempty"`
}
AdminPaths is the framework-owned URL surface the orchestrator drives. Self-describing so the orchestrator does not hardcode "/__nexus/...": if a future framework version moves them, this struct's emitted values change and the orchestrator follows.
func DefaultAdminPaths ¶ added in v0.23.0
func DefaultAdminPaths() AdminPaths
DefaultAdminPaths returns the framework's current admin URL layout. Build() uses this when Inputs.Admin has zero values, so any consumer that doesn't override gets the canonical paths. Callers that need to customize (e.g. a test that mounts admin under a different prefix) pass an Inputs.Admin with the relevant fields set.
type AppIdentity ¶ added in v0.23.0
type AppIdentity struct {
Name string `json:"name"`
// Version is the app's own version (typically `git describe` or a
// build-time -ldflags injection). Empty when the app didn't set one.
Version string `json:"version,omitempty"`
// NexusVersion is the framework version the binary was built against.
// Lets the orchestrator gate on framework features ("can this app
// answer /__nexus/drain?") without sniffing endpoints.
NexusVersion string `json:"nexusVersion,omitempty"`
// GeneratedAt is the wall-clock UTC time of manifest emission, in
// RFC3339. Excluded from ManifestHash.
GeneratedAt string `json:"generatedAt,omitempty"`
}
AppIdentity is the structured replacement for the flat Name/Version/ Deployment fields on Manifest. Carries the framework version it was built against and the wall-clock timestamp the manifest was emitted — useful for "when did this binary report itself?" but excluded from ManifestHash so two emissions of the same build hash-equal.
type Autoscale ¶ added in v0.39.0
type Autoscale struct {
Min int `json:"min,omitempty" toml:"min,omitempty"`
Max int `json:"max,omitempty" toml:"max,omitempty"`
}
Autoscale is the platform sizing hint per environment / deployment. Min/Max are replica counts; the platform decides metrics + policy.
type CORSBlock ¶ added in v0.49.0
type CORSBlock struct {
// AllowOrigins is the list of origins permitted to read responses.
// Entries can be exact ("https://app.example.com"), a wildcard
// ("*"), or a subdomain wildcard ("https://*.example.com").
AllowOrigins []string `json:"allowOrigins,omitempty" toml:"allow_origins,omitempty"`
// AllowMethods is the HTTP methods served. Defaults to GET/HEAD/
// POST/PUT/PATCH/DELETE when empty.
AllowMethods []string `json:"allowMethods,omitempty" toml:"allow_methods,omitempty"`
// AllowHeaders is request headers permitted on preflighted calls.
// Defaults to Accept/Content-Type/Authorization/X-Requested-With.
// Set to ["*"] to echo whatever the browser asks for (dev-friendly
// but loose).
AllowHeaders []string `json:"allowHeaders,omitempty" toml:"allow_headers,omitempty"`
// ExposeHeaders is response headers the browser surfaces to JS.
// Required to surface custom headers (X-Total-Count etc.) — the
// CORS-safelisted set is otherwise the only readable subset.
ExposeHeaders []string `json:"exposeHeaders,omitempty" toml:"expose_headers,omitempty"`
// AllowCredentials sets Access-Control-Allow-Credentials: true.
// Pointer-typed so the override path distinguishes "absent"
// (inherit) from "explicit false". Wildcard origin is incompatible
// with credentials; the cors extension rejects that pairing.
AllowCredentials *bool `json:"allowCredentials,omitempty" toml:"allow_credentials,omitempty"`
// MaxAge is the preflight cache lifetime in seconds. Defaults to
// 600 when zero. Production typically wants 86400 (24h); dev
// wants low values so policy edits take effect immediately.
MaxAge int `json:"maxAge,omitempty" toml:"max_age,omitempty"`
// Disabled, when true, makes the extension a no-op for this
// environment — useful when an upstream proxy / CDN already
// handles CORS.
Disabled bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
CORSBlock declares the CORS policy the binary wants applied to its public HTTP surface. Surfaced as a structured block (not env vars) so lint/doctor can sanity-check it — most notably the wildcard-vs-credentials trap that browsers silently reject.
environment_overrides typically uses this to swap origins per env: production locks to a specific frontend domain, preview environments are permissive ("*") so per-branch URLs work without manifest changes.
type CORSPatch ¶ added in v0.49.0
type CORSPatch struct {
AllowOrigins []string `json:"allowOrigins,omitempty" toml:"allow_origins,omitempty"`
AllowMethods []string `json:"allowMethods,omitempty" toml:"allow_methods,omitempty"`
AllowHeaders []string `json:"allowHeaders,omitempty" toml:"allow_headers,omitempty"`
ExposeHeaders []string `json:"exposeHeaders,omitempty" toml:"expose_headers,omitempty"`
AllowCredentials *bool `json:"allowCredentials,omitempty" toml:"allow_credentials,omitempty"`
MaxAge *int `json:"maxAge,omitempty" toml:"max_age,omitempty"`
Disabled *bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
CORSPatch is the override-adjustable subset of CORSBlock. Slice fields follow list-replace semantics (matches TLS) — per-env origin lists are typically wholly different, not deep-merged. Pointers on scalars let "absent" mean inherit.
type Check ¶ added in v0.44.0
Check is the function signature every diagnostic implements. Takes the effective manifest (post MergeOverrides), returns zero or more findings. Pure: same input always produces same output, so checks can be unit-tested in isolation without booting the framework.
type CronSummary ¶
type DeployTOMLInputs ¶ added in v0.82.0
type DeployTOMLInputs struct {
Environments map[string]EnvironmentTOML `toml:"environments,omitempty"`
Secrets map[string]Secret `toml:"secrets,omitempty"`
Files map[string]File `toml:"files,omitempty"`
Hooks *Hooks `toml:"hooks,omitempty"`
TLS *TLSBlock `toml:"tls,omitempty"`
CORS *CORSBlock `toml:"cors,omitempty"`
Errors *ErrorsBlock `toml:"errors,omitempty"`
EnvironmentOverrides map[string]Override `toml:"environment_overrides,omitempty"`
// Pre-existing top-level blocks (auto-populated by `nexus
// reconcile` from binary print mode, or hand-written by
// operators). Not part of the v0.42 "cloud inputs" surface but
// surfaced through the same loader so tools that consume TOML
// only (doctor, lint) see the complete declared shape.
Env map[string]EnvVar `toml:"env,omitempty"`
Services map[string]ServiceNeed `toml:"services,omitempty"`
}
DeployTOMLInputs is the on-disk shape of the cloud-inputs surface inside nexus.toml. Distinct from the framework's runtime Manifest type so the TOML schema can evolve independently of the JSON one the binary emits at NEXUS_PRINT_MANIFEST=1 print time — the two surfaces serve different consumers (operators vs. the platform) and benefit from different conventions (snake_case TOML keys, fewer required fields up-front).
Top-level keys map to nexus.toml entries the loader recognizes today. Unknown keys are ignored so old manifests parse without errors:
[environments.production]
domain = "app.example.com"
[environments.staging]
domain = "staging.example.com"
[secrets.JWT_SIGNING_KEY]
required = true
env_scoped = true
[files.tls_bundle]
path = "/etc/ssl/app/bundle.pem"
mode = 0o400
[hooks]
build = ["go build ./..."]
predeploy = ["./bin/app migrate"]
[environment_overrides.production]
env = { LOG_LEVEL = "warn" }
[environment_overrides.production.services.main_db]
size = "large"
backup = "hourly"
Maps are keyed by logical name (env name, secret name, etc.) for operator ergonomics. The loader converts to the framework's slice-of-named-structs shape at Load time so the runtime path stays unchanged.
type Deployment ¶ added in v0.23.0
type Deployment struct {
Name string `json:"name"` // matches NEXUS_DEPLOYMENT
Port int `json:"port,omitempty"`
// Owns is the list of module names assigned to this deployment.
// Modules with no DeployAs tag and no manifest assignment are
// "always local" — they appear under whichever deployment is active
// at runtime, and so are not listed here.
Owns []string `json:"owns,omitempty"`
// Peers is the list of OTHER deployment names this deployment
// declares as hard dependencies (must be reachable for ready=true).
Peers []string `json:"peers,omitempty"`
// Scaling is operator-supplied hints. Empty/zero values mean
// "platform default" — the orchestrator picks min=max=1.
Scaling Scaling `json:"scaling,omitzero"`
}
Deployment is one unit in the app's topology. A monolith app emits a single Deployment named "monolith"; a split app emits one per nexus.toml `deployments:` entry.
type EndpointSummary ¶
type Entity ¶ added in v0.23.0
type Entity struct {
Name string `json:"name"`
Module string `json:"module,omitempty"`
TenantScoped bool `json:"tenantScoped,omitempty"`
Ops []string `json:"ops,omitempty"` // "create","read","update","delete","list"
Fields []EntityField `json:"fields,omitempty"`
}
Entity is one crud-registered type. Drives the dashboard's per-tenant data browser and the orchestrator's "schema changed" diff-on-deploy view.
type EntityField ¶ added in v0.23.0
type EntityField struct {
Name string `json:"name"`
Type string `json:"type"`
PrimaryKey bool `json:"primaryKey,omitempty"`
Indexed bool `json:"indexed,omitempty"`
Unique bool `json:"unique,omitempty"`
Nullable bool `json:"nullable,omitempty"`
}
EntityField is one column on an Entity. v1 captures name + type + the three flags an operator-facing data browser needs (PK to render row identity, Indexed/Unique to hint at safe filter columns). Foreign keys / cascading rules are deferred until v1.x — no consumer needs them today and modeling them well requires lock-in on the crud package's relation API.
type EnvProvider ¶
type EnvProvider interface {
NexusEnv() []EnvVar
}
EnvProvider is implemented by anything that reads configuration from the environment and wants those reads documented in /__nexus/manifest. Typical implementer: a *Config struct's constructor, or a manager like *cache.Manager whose Start() reads REDIS_HOST/PORT/PASSWORD.
type EnvValidation ¶ added in v0.39.0
type EnvValidation struct {
Enum []string `json:"enum,omitempty" toml:"enum,omitempty"`
Regex string `json:"regex,omitempty" toml:"regex,omitempty"`
Min *int `json:"min,omitempty" toml:"min,omitempty"` // numeric values
Max *int `json:"max,omitempty" toml:"max,omitempty"` // numeric values
Length *Range `json:"length,omitempty" toml:"length,omitempty"` // string length
}
EnvValidation constrains the effective value of an env var or secret. Applied at boot (or pre-render, when the platform is doing the merge). All fields are optional and AND together.
type EnvVar ¶
type EnvVar struct {
Name string `json:"name" toml:"name"`
Description string `json:"description,omitempty" toml:"description,omitempty"`
Required bool `json:"required,omitempty" toml:"required,omitempty"`
Secret bool `json:"secret,omitempty" toml:"secret,omitempty"`
Default string `json:"default,omitempty" toml:"default,omitempty"`
// BoundTo is a dotted reference into a ServiceNeed entry, e.g.
// "primary-db.host" means "fill this env var with the resolved host
// of the ServiceNeed named primary-db". Empty when the operator
// must supply the value directly.
BoundTo string `json:"boundTo,omitempty" toml:"bound_to,omitempty"`
// EnvScoped means each environment gets its own value. Typical for
// LOG_LEVEL or feature flags that legitimately differ per env.
// When false (the default), one value applies across every
// environment that doesn't override it.
EnvScoped bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
// Validation constrains the effective value. Applied at boot or
// pre-render. nil = no validation beyond Required.
Validation *EnvValidation `json:"validation,omitempty" toml:"validation,omitempty"`
// Source tracks where the effective value came from after merge:
// "default" | "override" | "platform" | "env". Populated by
// MergeOverrides; empty in the declared base manifest.
Source string `json:"source,omitempty" toml:"source,omitempty"`
}
EnvVar is one configuration knob the app reads. Required signals "boot won't proceed without this"; Secret signals "store this in a secret manager, not the deploy YAML". BoundTo is a hint for orchestration: when a ServiceNeed exposes "host" via this var, the platform can fill it automatically without operator intervention.
type EnvVarPatch ¶ added in v0.39.0
type EnvVarPatch struct {
Description *string `json:"description,omitempty" toml:"description,omitempty"`
Required *bool `json:"required,omitempty" toml:"required,omitempty"`
Default *string `json:"default,omitempty" toml:"default,omitempty"`
EnvScoped *bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
Validation *EnvValidation `json:"validation,omitempty" toml:"validation,omitempty"`
}
EnvVarPatch is the subset of EnvVar fields an override is allowed to adjust. Pointer fields let "absent" mean "inherit", while non-nil pointer means "set". Default and EnvScoped and Validation can all be adjusted; Name and Source cannot.
type Environment ¶ added in v0.39.0
type Environment struct {
Name string `json:"name" toml:"name"`
Domain string `json:"domain,omitempty" toml:"domain,omitempty"`
Autoscale *Autoscale `json:"autoscale,omitempty" toml:"autoscale,omitempty"`
// TTL marks the environment as ephemeral (preview/PR deploys).
// Format: Go duration string ("7d" requires the platform's parser;
// time.ParseDuration accepts hours/minutes/seconds only). Empty =
// persistent environment.
TTL string `json:"ttl,omitempty" toml:"ttl,omitempty"`
}
Environment is one named target the binary can be deployed to — "production", "staging", "preview", etc. Overrides reference it by name; the orchestration platform routes deploys at the environment level.
type EnvironmentTOML ¶ added in v0.82.0
type EnvironmentTOML struct {
Domain string `toml:"domain,omitempty"`
Autoscale *Autoscale `toml:"autoscale,omitempty"`
TTL string `toml:"ttl,omitempty"`
}
EnvironmentTOML mirrors Environment but doesn't carry a Name field (the table key in [environments.X] is the name). Lifting Name out of the value keeps the operator-facing TOML clean:
[environments.production] domain = "app.example.com"
rather than the redundant:
[[environments]] name = "production" domain = "app.example.com"
type ErrCode ¶ added in v0.39.0
type ErrCode string
ErrCode classifies merge failures. Stable identifiers so the lint step and the CLI can format error messages consistently.
const ( ErrUnknownEnvironment ErrCode = "unknown_environment" ErrUnknownOverrideKey ErrCode = "unknown_override_key" ErrConflictingOverride ErrCode = "conflicting_override" ErrInvalidValidation ErrCode = "invalid_validation" ErrTypeMismatch ErrCode = "type_mismatch" ErrMissingDeclaration ErrCode = "missing_declaration" )
type ErrorsBlock ¶ added in v0.50.0
type ErrorsBlock struct {
// Environment is the tag every reported event carries
// ("production", "staging", "preview"). Empty in dev.
Environment string `json:"environment,omitempty" toml:"environment,omitempty"`
// Release identifies the binary version. Typical wiring is
// either a Go ldflags-injected variable or the GIT_SHA env var.
Release string `json:"release,omitempty" toml:"release,omitempty"`
// ServerName is the host that captured the event. Defaults to
// os.Hostname() when empty.
ServerName string `json:"serverName,omitempty" toml:"server_name,omitempty"`
// Capacity is the in-memory ring-buffer size for the dashboard.
// 0 means "use default" (100).
Capacity int `json:"capacity,omitempty" toml:"capacity,omitempty"`
// SampleRate is the fraction of captured events forwarded to
// transports. Pointer-typed so the override path distinguishes
// "absent" (inherit) from "explicit 0.0" (drop everything).
// Domain: [0.0, 1.0]. Default: 1.0.
SampleRate *float64 `json:"sampleRate,omitempty" toml:"sample_rate,omitempty"`
// IgnorePaths is a list of request paths whose errors are NOT
// captured. Default: ["/__nexus/health", "/__nexus/ready"].
IgnorePaths []string `json:"ignorePaths,omitempty" toml:"ignore_paths,omitempty"`
// Disabled, when true, makes the plugin a no-op for this
// environment.
Disabled bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
ErrorsBlock declares the configuration for the extension/errors plugin — environment / release tags, capacity, sample rate, and the ignore-paths list. Transports stay in code (they carry Go-only http.Client / function types that can't survive a YAML round-trip).
Per-env overrides are the typical use: production reports at 100% to Sentry; preview environments sample at 10%; internal envs flip Disabled to avoid double-reporting through another stack.
type ErrorsPatch ¶ added in v0.50.0
type ErrorsPatch struct {
Environment *string `json:"environment,omitempty" toml:"environment,omitempty"`
Release *string `json:"release,omitempty" toml:"release,omitempty"`
ServerName *string `json:"serverName,omitempty" toml:"server_name,omitempty"`
Capacity *int `json:"capacity,omitempty" toml:"capacity,omitempty"`
SampleRate *float64 `json:"sampleRate,omitempty" toml:"sample_rate,omitempty"`
IgnorePaths []string `json:"ignorePaths,omitempty" toml:"ignore_paths,omitempty"`
Disabled *bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
ErrorsPatch is the per-environment override subset of ErrorsBlock. Slice fields list-replace; scalar fields use pointer-set semantics so an override can distinguish "absent" from "explicit zero".
type File ¶ added in v0.39.0
type File struct {
Name string `json:"name" toml:"name"`
Path string `json:"path" toml:"path"`
Description string `json:"description,omitempty" toml:"description,omitempty"`
// Mode is the octal file mode (e.g. 0400 = read-only owner). 0
// means the platform picks (typically 0644).
Mode int `json:"mode,omitempty" toml:"mode,omitempty"`
// Secret=true → encrypt at rest, redact in any UI rendering of
// the file's contents.
Secret bool `json:"secret,omitempty" toml:"secret,omitempty"`
EnvScoped bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
// JSONSchema is the optional shape the file's contents must
// satisfy (Draft 2020-12). The platform validates pre-deploy and
// rejects an override that violates the schema.
JSONSchema json.RawMessage `json:"schema,omitempty" toml:"schema,omitempty"`
}
File is a mounted blob — TLS bundles, JSON config overrides, etc. Larger than an env var would carry cleanly. The platform writes the bytes to Path at deploy time; Mode controls filesystem permissions.
type FilePatch ¶ added in v0.39.0
type FilePatch struct {
Description *string `json:"description,omitempty" toml:"description,omitempty"`
Mode *int `json:"mode,omitempty" toml:"mode,omitempty"`
Secret *bool `json:"secret,omitempty" toml:"secret,omitempty"`
EnvScoped *bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
}
FilePatch is the override-adjustable subset of File.
type Finding ¶ added in v0.44.0
type Finding struct {
Severity Severity `json:"severity"`
Code string `json:"code"`
Path string `json:"path"`
Message string `json:"message"`
Hint string `json:"hint,omitempty"`
}
Finding is one diagnostic from Doctor. Mirrors lint.Issue's shape but distinct so the two tools can evolve independently — Doctor's findings often span multiple sections (e.g. "env X references service Y which doesn't exist"), Lint's are section-local.
func Doctor ¶ added in v0.44.0
Doctor runs a battery of configuration-coherence checks against an effective Manifest and returns every finding. Distinct from Lint (manifest.Lint), which validates SHAPE — Doctor validates SETUP COHERENCE:
- Lint catches "this YAML key is misspelled / type-wrong".
- Doctor catches "this configuration won't work even though it's well-formed" — required env without source, service declared without expose_as, dashboard enabled but introspection closed, etc.
Findings are sorted by severity (errors before warnings before info) then by path so output is stable across runs — important for CI integration that diffs the report.
type Frontend ¶ added in v0.23.0
type Frontend struct {
Embedded bool `json:"embedded"`
MountPath string `json:"mountPath,omitempty"`
// BuildHash is content hash of the embedded asset bundle. Empty
// when the build pipeline didn't inject one. Orchestrator uses it
// to decide whether a CDN invalidation is needed across deploys.
BuildHash string `json:"buildHash,omitempty"`
}
Frontend describes an embedded SPA served by nexus.ServeFrontend. Absent (Manifest.Frontend == nil) when the app serves no frontend — don't emit an empty Frontend{} struct, the omitempty on Manifest handles it.
type Health ¶
type Health struct {
Liveness string `json:"liveness"` // always "/__nexus/health" today
Readiness string `json:"readiness"` // always "/__nexus/ready"
App string `json:"app,omitempty"` // optional app-level probe
}
Health is the probe map. Each path is rooted at the app's external URL; the orchestration platform appends them to the public Port for liveness / readiness HTTP probes.
type Hooks ¶ added in v0.39.0
type Hooks struct {
// Build runs on the platform's build node before the image is
// finalized. Typical use: `go build`, `npm run build`, vendoring.
Build []string `json:"build,omitempty" toml:"build,omitempty"`
// Predeploy runs after the new image is built and before traffic
// shifts. Typical use: schema migrations, cache warmups.
Predeploy []string `json:"predeploy,omitempty" toml:"predeploy,omitempty"`
// Postdeploy runs after the new release is live. Typical use: CDN
// purges, Slack notifications, smoke tests against the new URL.
Postdeploy []string `json:"postdeploy,omitempty" toml:"postdeploy,omitempty"`
}
Hooks are platform-orchestrated commands run outside the binary at well-defined deploy phases. Each list runs sequentially; non-zero exit aborts the deploy. Env vars resolved through the same merge pipeline are available to every hook command.
type Inputs ¶
type Inputs struct {
Name string
Version string
Deployment string
Ports []Port
AppHealth string // optional app-level probe path; "" omits the field
// NexusVersion is the framework version the binary was built
// against. The framework injects it (typically via -ldflags or a
// build-time constant in the nexus package); leaving it empty is
// fine for tests.
NexusVersion string
EnvProviders []EnvProvider
ServiceProviders []ServiceDependencyProvider
VolumeProviders []VolumeProvider
StartupTasks []StartupTask
// Direct registrations bypass the provider walk — used by
// app.UseVolume(...) and any future app.DeclareEnv(...) calls
// that don't go through an interface.
DirectEnv []EnvVar
DirectServices []ServiceNeed
DirectVolumes []Volume
Workers []WorkerSummary
Crons []CronSummary
Endpoints []EndpointSummary
// v1 structured inputs ─────────────────────────────────────────
// The framework will populate these as registry/topology
// integration lands; leaving them nil is supported and produces
// empty top-level slices on the resulting Manifest.
Deployments []Deployment
Modules []Module
Routes []Route
Entities []Entity
// Frontend is non-nil when the app called nexus.ServeFrontend.
Frontend *Frontend
// Admin overrides DefaultAdminPaths(). Zero value (all fields
// empty) means "use defaults"; setting any field overrides only
// that path. Build() merges Admin field-by-field with defaults.
Admin AdminPaths
// Cloud / inputs surface (v0.39+). The framework wires these from
// the new DeclareEnvironment / DeclareSecret / DeclareFile /
// DeclareHooks / DeclareOverride APIs; nil/empty is the supported
// default for pre-cloud apps.
Environments []Environment
DirectSecrets []Secret
DirectFiles []File
Hooks *Hooks
Overrides map[string]Override
// Plugin-driven blocks read from nexus.toml. Each plugin's
// config (tls/cors/errors) lives at the top level of the YAML and
// is consumed by the extension at boot via app.EffectiveManifest.
// Nil = not declared; the corresponding plugin falls back to its
// in-code Config struct.
TLS *TLSBlock
CORS *CORSBlock
Errors *ErrorsBlock
}
Inputs is the data the framework collects from various places before rendering the manifest. Building it lives outside this package (the *App has the references); we accept it as a struct so this package has no dependency on nexus internals — meaning it stays unit-testable in isolation and won't create an import cycle.
Most fields are slices the framework appends to as providers are discovered. Echoed sections (Workers/Crons/Endpoints) are read-only snapshots from the registry.
type Issue ¶ added in v0.39.0
Issue is one lint finding. Path is a dotted manifest path to the offending field so editors / CI output can deep-link. Code mirrors MergeError codes where applicable so tooling can share formatters.
func Lint ¶ added in v0.39.0
Lint walks the manifest and returns every issue it finds in stable, sorted order. The function is pure: same input always produces the same output. Callers (the nexus CLI's `lint` subcommand, future IDE integrations, CI gates) treat any Error-severity issue as a hard failure.
Lint catches:
- Override references an environment that isn't declared.
- Override key (env / secret / file / service / volume) that doesn't exist in the base manifest.
- Scalar lock and spec diff on the same env key.
- Validation rules that are themselves malformed (regex that doesn't compile, Length.Min > Length.Max, empty Enum slice).
- Default value that violates the input's own Validation.
- Required env / secret with no Default and no platform binding.
- Duplicate names within a single input slice.
- BoundTo pointing to a nonexistent service or unknown field.
- Duplicate environment names.
The first three overlap with what MergeOverrides checks at merge time; lint catches them earlier so the CLI can flag them on save.
type Manifest ¶
type Manifest struct {
// Schema + integrity ───────────────────────────────────────────
// SchemaVersion is the contract version (see SchemaV1). Always
// emitted; absence in a parsed document means the producer is
// pre-v1 and the consumer should treat the document as best-effort.
SchemaVersion string `json:"schemaVersion"`
// ManifestHash is sha256 hex over the canonical JSON of every
// other field, EXCEPT App.GeneratedAt (which would otherwise make
// every emission unique). Stable across re-emissions of the same
// build, so an orchestrator can use it as a cache key and a
// "redeploy needed?" signal. Format: "sha256:<hex>".
ManifestHash string `json:"manifestHash,omitempty"`
// App identity ─────────────────────────────────────────────────
// App is the structured replacement for the flat Name/Version/
// Deployment fields. Both are populated for back-compat; v2 will
// drop the flat fields. NexusVersion + GeneratedAt are new and
// have no flat equivalent.
App AppIdentity `json:"app"`
// Identity (v0 back-compat — duplicated into App above) ────────
Name string `json:"name"`
Version string `json:"version,omitempty"` // *App.Version()
Deployment string `json:"deployment,omitempty"` // *App.Deployment(); "" = monolith
// Network ──────────────────────────────────────────────────────
Ports []Port `json:"ports,omitempty"`
// Health probes the orchestration platform should hit. Liveness +
// Readiness are framework-owned (always /__nexus/health and
// /__nexus/ready); App is an optional app-level probe — the
// orchestration platform's "container ready" gate after the
// framework probes pass.
Health Health `json:"health"`
// Configuration the operator must (or may) supply. Aggregated
// from every registered EnvProvider; deduplicated by Name with
// last-writer-wins on description / required / secret.
Env []EnvVar `json:"env,omitempty"`
// Backing services to provision. Each entry names a logical sidecar
// (a Postgres, a Redis, etc.); the orchestration platform decides
// how to satisfy it (managed sidecar, external pool, etc.) and
// injects the env vars listed in ExposeAs.
Services []ServiceNeed `json:"services,omitempty"`
// Writable paths the container needs preserved across restarts.
Volumes []Volume `json:"volumes,omitempty"`
// One-shot tasks run before the HTTP listener binds. Migrations,
// schema sync, seed data. Failure halts boot.
StartupTasks []StartupTask `json:"startupTasks,omitempty"`
// Echoed from the existing registry, included here so a deployer
// reads ONE document. The dashboard's other endpoints continue to
// serve their richer per-domain views — this is the deploy-time
// projection.
Workers []WorkerSummary `json:"workers,omitempty"`
Crons []CronSummary `json:"crons,omitempty"`
Endpoints []EndpointSummary `json:"endpoints,omitempty"`
// Deployments is the topology declared in nexus.toml: every
// deployment unit, what modules it owns, its peers, scaling hints.
// The framework's print mode does not read nexus.toml — the
// `nexus build --emit-manifest` tool merges the YAML topology into
// the manifest before writing it out. Single-deployment ("monolith")
// apps still get one entry here for uniformity.
Deployments []Deployment `json:"deployments,omitempty"`
// Modules is the per-module view: which deployment owns each module
// and what routes/crons/entities it contributes. The Routes/Crons/
// Entities fields here hold IDs into the top-level slices so the
// orchestrator can filter without walking nested structures.
Modules []Module `json:"modules,omitempty"`
// Routes is the enriched view of every HTTP/GraphQL/WS surface,
// including module + deployment tags and auth/tenant metadata.
// Supersedes Endpoints; both are populated for back-compat.
Routes []Route `json:"routes,omitempty"`
// Entities is the crud-registered type catalog: name, fields,
// supported operations, tenant scope. Drives the dashboard's
// per-tenant data browser.
Entities []Entity `json:"entities,omitempty"`
// Frontend is non-nil when the app serves an embedded SPA
// (nexus.ServeFrontend). The orchestrator uses BuildHash to gate
// CDN invalidation on actual asset changes.
Frontend *Frontend `json:"frontend,omitempty"`
// Admin lists the framework-owned admin URL paths. Self-describing
// so the orchestrator does not hardcode "/__nexus/..." — if a
// future framework version moves them, the manifest is the source
// of truth.
Admin AdminPaths `json:"admin"`
// Environments enumerates every named target ("production",
// "staging", "preview", ...) the binary can be deployed to. The
// orchestration platform owns the actual provisioning; this slice
// declares the contract.
Environments []Environment `json:"environments,omitempty"`
// Secrets are sensitive inputs (API keys, signing keys, etc.) that
// live in the platform's encrypted store, distinct from regular
// env vars so the platform UI can render them with redaction +
// rotation hints. Empty for apps without sensitive inputs.
Secrets []Secret `json:"secrets,omitempty"`
// Files are mounted blobs (TLS bundles, JSON config overrides) the
// platform writes to known paths at deploy time. Empty for apps
// without file-shaped inputs.
Files []File `json:"files,omitempty"`
// Hooks declares platform-orchestrated build/predeploy/postdeploy
// commands. Nil → no platform-side hooks; in-binary StartupTasks
// (above) still apply.
Hooks *Hooks `json:"hooks,omitempty"`
// TLS declares the public-internet TLS configuration the binary
// wants. Read by the extension/tls plugin at boot for the active
// environment's effective values (after MergeOverrides). Nil =
// no platform-managed TLS requested. See TLSBlock.
TLS *TLSBlock `json:"tls,omitempty"`
// CORS declares the cross-origin policy the binary applies to
// its public HTTP surface. Read by the extension/cors plugin at
// boot. Nil = no manifest-driven CORS (in-code Config still
// applies if the plugin is loaded). See CORSBlock.
CORS *CORSBlock `json:"cors,omitempty"`
// Errors declares the configuration for the extension/errors
// plugin (capture + reporting policy). Nil = no manifest-driven
// errors config; in-code Config still applies. See ErrorsBlock.
Errors *ErrorsBlock `json:"errors,omitempty"`
// Overrides hold the per-environment diffs applied to the base
// inputs at merge time. Keyed by environment name. After
// MergeOverrides runs, the returned Manifest has Overrides nil —
// the diffs have been baked into the effective values.
Overrides map[string]Override `json:"overrides,omitempty"`
}
Manifest is the full self-description an external deployer reads. All slices are nil-able (omitempty) so an app that declares nothing still produces a valid, minimal document.
Field ordering: SchemaVersion + ManifestHash come first so consumers reading the JSON head can gate on version + diff on hash without parsing the whole document. App identity follows. Then the v0 back-compat top-level fields (Name/Version/Deployment, Ports, Health, Env, Services, Volumes, StartupTasks, Workers, Crons, Endpoints) — kept populated so existing readers (orchestrators already in production) stay green. Finally the v1 structured sections (Deployments, Modules, Routes, Entities, Frontend, Admin) — new readers consume these in preference to the flat back-compat fields.
func Build ¶
Build aggregates a Manifest from Inputs. Deduplicates env vars by Name (last writer wins on metadata fields, but Required is OR-ed across declarations and Secret is OR-ed too — once flagged secret, always secret). Sorts everything for deterministic output so /__nexus/manifest is stable across boots and easy to diff in CI.
SchemaVersion is set to SchemaV1, App identity is filled from Inputs (with GeneratedAt = now UTC), AdminPaths fall back to DefaultAdminPaths() field-by-field, and ManifestHash is computed last over the canonical JSON of every other field (excluding App.GeneratedAt, so two emissions of the same build hash-equal).
func LoadInputsTOML ¶ added in v0.82.0
LoadInputsTOML parses a TOML document into a partial Manifest with only the inputs surface populated (Environments, Secrets, Files, Hooks, Overrides). The other Manifest fields stay zero-valued — callers merge this result with the framework-built base manifest.
Behavior:
- Top-level keys outside the inputs surface (deployments, peers, services from reconcile, etc.) are silently ignored. The same file can declare both surfaces without conflict.
- Table-keyed inputs are converted to named-struct slices: the TOML table key fills the Name field of each entry.
- Empty / missing sections produce nil slices and nil pointers (no allocations), so the result round-trips identically through MergeOverrides as if those sections were never declared.
- ${VAR} and ${VAR:default} placeholders inside basic-string values are expanded from os.Environ BEFORE the TOML parser sees the document. Strict mode: a token without a default whose env var is unset / empty fails the load.
Returns an error on TOML syntax problems or unresolved placeholders. Schema validation (duplicate names, malformed validation rules, unknown override keys) lives in Lint — callers should run Lint after Load.
func LoadInputsTOMLFile ¶ added in v0.82.0
LoadInputsTOMLFile is the file-path convenience over LoadInputsTOML. Reads the file then delegates. Errors include the path so a missing file or TOML syntax problem is easy to locate.
func MergeOverrides ¶ added in v0.39.0
MergeOverrides applies the per-environment Override block to the base Manifest's inputs (Env / Secrets / Files / Services / Hooks) and returns the effective manifest for env. The returned value is a deep copy of base with diffs applied and Overrides cleared — callers can treat it as the resolved contract for the active environment.
The function is pure: same (base, env) always produces the same output. Both the orchestration platform (pre-rendering values before deploy) and the framework (self-hosted boot resolution) call into this single implementation so values stay byte-equivalent.
Resolution rules (from the inputs.go doc):
- Scalar lock (Override.Env[KEY] = "warn") replaces the effective default and marks the field locked.
- Spec diff (Override.EnvSpecs[KEY] = {Default: warn}) merges into the base EnvVar field by field; absent (nil) pointers inherit.
- Removal (Override.Removed = ["secrets.STRIPE_API_KEY"]) drops the entry from the effective slice.
- Service deep-merge (Override.Services[name] = {Size: large, ExposeAs: {password: DB_PASSWORD}}) replaces named fields and adds new ExposeAs map entries; unmentioned base fields inherit.
- Hooks block (Override.Hooks) wholesale replaces base.Hooks for this environment when non-nil. Nil = inherit.
Errors:
- env not declared in base.Environments → ErrUnknownEnvironment (so a typo doesn't silently no-op).
- Override key absent from base inputs → ErrUnknownOverrideKey (overrides may only adjust declared inputs, never introduce them — keeps the base contract authoritative).
- Override.Env scalar lock on a key that also has a spec diff → ErrConflictingOverride.
- Removed entry that doesn't exist in base → ErrUnknownOverrideKey (consistency with the other rejections).
type MergeError ¶ added in v0.39.0
MergeError carries a code + human message. Implements error so callers can type-assert or just print.
func (*MergeError) Error ¶ added in v0.39.0
func (e *MergeError) Error() string
type Module ¶ added in v0.23.0
type Module struct {
Name string `json:"name"`
// Deployment is the resolved DeployAs tag (explicit or inferred from
// nexus.toml `owns`). Empty string means "always local" —
// runs in whichever deployment is active.
Deployment string `json:"deployment,omitempty"`
// Package is the Go import path of the module, used for dashboard
// deep-links to source. Empty when the framework can't infer it.
Package string `json:"package,omitempty"`
Routes []string `json:"routes,omitempty"` // route IDs
Crons []string `json:"crons,omitempty"` // cron names
Entities []string `json:"entities,omitempty"` // entity names
}
Module is the per-module view of an app. The Routes/Crons/Entities fields hold IDs into the top-level Manifest.Routes/Crons/Entities slices — flat references over nesting so the orchestrator can index once and filter cheaply.
type Override ¶ added in v0.39.0
type Override struct {
// Scalar locks live in Env / SecretLocks / FileLocks. Object diffs
// (validation changes, required flips, expose_as additions) live
// in EnvSpecs / SecretSpecs / FileSpecs / Services. Null removals
// live in Removed. Splitting by shape sidesteps JSON's any-typed
// nightmare for merge code.
Env map[string]string `json:"env,omitempty" toml:"env,omitempty"`
EnvSpecs map[string]EnvVarPatch `json:"envSpecs,omitempty" toml:"env_specs,omitempty"`
SecretSpecs map[string]SecretPatch `json:"secretSpecs,omitempty" toml:"secret_specs,omitempty"`
FileSpecs map[string]FilePatch `json:"fileSpecs,omitempty" toml:"file_specs,omitempty"`
Services map[string]ServicePatch `json:"services,omitempty" toml:"services,omitempty"`
// Removed holds dotted paths (e.g. "secrets.STRIPE_API_KEY",
// "files.tls_bundle") that don't exist in this environment.
Removed []string `json:"removed,omitempty" toml:"removed,omitempty"`
// Hooks fully replaces the base Hooks block for this environment.
// nil means inherit; non-nil replaces.
Hooks *Hooks `json:"hooks,omitempty" toml:"hooks,omitempty"`
// TLS patches the base TLSBlock. Field-by-field merge — Domains
// is a list-replace, everything else is pointer-set. nil means
// "inherit base TLS unchanged". Operators typically use this to
// swap domains (production app.example.com vs staging.example.com)
// and to flip Staging=true in non-production environments.
TLS *TLSPatch `json:"tls,omitempty" toml:"tls,omitempty"`
// CORS patches the base CORSBlock. Same merge shape as TLS —
// slice fields list-replace, scalars pointer-set. Typical use
// is locking AllowOrigins to a per-environment frontend URL.
CORS *CORSPatch `json:"cors,omitempty" toml:"cors,omitempty"`
// Errors patches the base ErrorsBlock. Common override is
// SampleRate (cut preview noise) or Disabled (silence a
// specific environment).
Errors *ErrorsPatch `json:"errors,omitempty" toml:"errors,omitempty"`
}
Override is the per-environment diff applied on top of the base inputs at merge time. Keys absent here inherit from base; keys with scalar values lock the field (operator UI marks it read-only); keys with object values deep-merge into the base spec; explicit null removes the field from the effective manifest for this environment.
Overrides cannot introduce inputs that aren't declared in the base — the lint step rejects that. The base manifest is the authoritative answer to "what does this binary need?".
type Port ¶
type Port struct {
Name string `json:"name"` // e.g. "http", "admin"
Port int `json:"port"` // numeric only — Addr ":9390" → 9390
Scope string `json:"scope,omitempty"` // matches nexus.ListenerScope strings
}
Port describes one listening socket. The orchestration platform uses these to publish container ports + decide which to expose externally.
Scope mirrors nexus's listener scope ("public" | "admin" | "internal"); orchestration treats anything other than "public" as cluster-internal by default.
type Range ¶ added in v0.39.0
type Range struct {
Min *int `json:"min,omitempty" toml:"min,omitempty"`
Max *int `json:"max,omitempty" toml:"max,omitempty"`
}
Range is [Min, Max] inclusive. Nil pointers mean unbounded on that end; using pointer-int lets the zero value be a meaningful bound.
type Registrar ¶ added in v0.22.2
type Registrar interface {
DeclareEnvProvider(EnvProvider)
DeclareServiceProvider(ServiceDependencyProvider)
DeclareVolumeProvider(VolumeProvider)
}
Registrar is the contract *nexus.App satisfies for the manifest declaration methods. Carved into the leaf manifest package so any package (cache, db, app-defined wrappers) can require it as an fx dependency without importing nexus and creating an import cycle.
Implementations must accept nil-safe registration: calling with a nil provider is a no-op, not a panic. (Mirrors the *App method guards.)
type Route ¶ added in v0.23.0
type Route struct {
ID string `json:"id"`
// Kind is the route taxonomy:
// "rest" — Method + Path
// "graphql.query" — Operation
// "graphql.mutation" — Operation
// "graphql.subscription" — Operation
// "ws" — Path
Kind string `json:"kind"`
Module string `json:"module,omitempty"`
Deployment string `json:"deployment,omitempty"`
Method string `json:"method,omitempty"` // REST only
Path string `json:"path,omitempty"` // REST/WS only
Operation string `json:"operation,omitempty"` // GraphQL only
// Auth is "none" | "optional" | "required". Empty defaults to
// "none" on the consumer side; emitters should set it explicitly.
Auth string `json:"auth,omitempty"`
TenantScoped bool `json:"tenantScoped,omitempty"`
}
Route is the enriched HTTP/GraphQL/WS surface descriptor. Supersedes EndpointSummary, which is kept on Manifest for back-compat. ID is stable across re-emissions of the same build (computed from kind+method+path+operation) so cross-references from Module.Routes stay valid.
type Scaling ¶ added in v0.23.0
Scaling is the v1 scaling hint shape. Kept intentionally small so the contract stays stable; richer policies (CPU/memory/queue-depth triggers) are deferred until the scaler module has real policies and can extend this struct additively.
type Secret ¶ added in v0.39.0
type Secret struct {
Name string `json:"name" toml:"name"`
Description string `json:"description,omitempty" toml:"description,omitempty"`
Required bool `json:"required,omitempty" toml:"required,omitempty"`
// EnvScoped means each environment gets its own value (typical for
// API keys that differ between prod/staging). false → one value
// shared across every environment (rare; usually a misconfiguration).
EnvScoped bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
// RotateAlert is a platform reminder: emit a warning once the
// secret hasn't been written for this long. Go-duration format on
// the framework side; platform parses ("90d" etc.) per its own
// rules. Optional.
RotateAlert string `json:"rotateAlert,omitempty" toml:"rotate_alert,omitempty"`
Validation *EnvValidation `json:"validation,omitempty" toml:"validation,omitempty"`
}
Secret is a sensitive input the operator supplies through the platform's encrypted store. Distinct from EnvVar so the platform UI can render secrets with redacted values + rotation reminders, and so the manifest hash doesn't entangle with secret values.
type SecretPatch ¶ added in v0.39.0
type SecretPatch struct {
Description *string `json:"description,omitempty" toml:"description,omitempty"`
Required *bool `json:"required,omitempty" toml:"required,omitempty"`
EnvScoped *bool `json:"envScoped,omitempty" toml:"env_scoped,omitempty"`
RotateAlert *string `json:"rotateAlert,omitempty" toml:"rotate_alert,omitempty"`
Validation *EnvValidation `json:"validation,omitempty" toml:"validation,omitempty"`
}
SecretPatch is the override-adjustable subset of Secret.
type ServiceDependencyProvider ¶
type ServiceDependencyProvider interface {
NexusServices() []ServiceNeed
}
ServiceDependencyProvider is implemented by anything that declares a logical sidecar requirement. Returning multiple is fine — a single "events" module might need both RabbitMQ (queue) and Postgres (idempotency table).
type ServiceNeed ¶
type ServiceNeed struct {
Name string `json:"name" toml:"name"` // unique within the app, e.g. "primary-db"
Kind string `json:"kind" toml:"kind"` // "postgres" | "redis" | "rabbitmq" | "s3" | ...
Version string `json:"version,omitempty" toml:"version,omitempty"` // major or constraint, e.g. "16", ">=14"
// ExposeAs maps logical fields (host, port, user, password, url, ...)
// to env-var names the app reads. The orchestration platform fills
// each one once the sidecar is bound. Field names are advisory but
// the well-known set is "host", "port", "user", "password", "url",
// "database", "vhost", "exchange".
ExposeAs map[string]string `json:"exposeAs,omitempty" toml:"expose_as,omitempty"`
// Optional indicates the app degrades gracefully without this
// sidecar (e.g. a Redis cache that falls back to in-memory). The
// platform may skip provisioning in dev environments.
Optional bool `json:"optional,omitempty" toml:"optional,omitempty"`
// Size is the platform-defined sizing tier ("small" | "medium" |
// "large" — exact set is platform-specific). Empty = platform
// default. Typically overridden per environment ("large" in prod,
// "small" in preview).
Size string `json:"size,omitempty" toml:"size,omitempty"`
// Backup is the platform-defined backup cadence ("none" | "daily"
// | "hourly" | "continuous"). Empty = platform default. Mostly
// relevant for stateful services (postgres, mysql); meaningless
// for caches.
Backup string `json:"backup,omitempty" toml:"backup,omitempty"`
// Ephemeral=true tells the platform to tear down the provisioned
// resource when the environment is destroyed. Used for preview
// environments so per-PR databases don't accumulate.
Ephemeral bool `json:"ephemeral,omitempty" toml:"ephemeral,omitempty"`
}
ServiceNeed is a logical sidecar this app needs to talk to. The orchestration platform decides provisioning policy. Kind is intentionally a string (not enum) so apps can declare bespoke dependencies the platform may not natively support yet — operators still see the requirement and can wire something manually.
type ServicePatch ¶ added in v0.39.0
type ServicePatch struct {
Version *string `json:"version,omitempty" toml:"version,omitempty"`
Size *string `json:"size,omitempty" toml:"size,omitempty"`
Backup *string `json:"backup,omitempty" toml:"backup,omitempty"`
ExposeAs map[string]string `json:"exposeAs,omitempty" toml:"expose_as,omitempty"`
// Ephemeral=true tells the platform to tear down the provisioned
// resource when the environment is destroyed (typical for preview
// envs where you don't want orphaned databases lying around).
Ephemeral *bool `json:"ephemeral,omitempty" toml:"ephemeral,omitempty"`
// Optional flips a service to non-required for this environment
// (e.g. payment service in preview).
Optional *bool `json:"optional,omitempty" toml:"optional,omitempty"`
}
ServicePatch is the override-adjustable subset of ServiceNeed. Most useful for sizing/backup tweaks per environment (large in prod, small in preview) and for adding new ExposeAs entries.
type Severity ¶ added in v0.39.0
type Severity string
Severity categorizes a lint issue. Error means the manifest is structurally broken — any consumer (merge, platform, framework boot) will fail or produce wrong results. Warning means the shape is legal but suspicious; the user should review.
type StartupTask ¶
type StartupTask struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
// Phase is "pre-start" today; reserved for "post-start", "pre-stop"
// future expansion. Encoded as a string for forward compatibility.
Phase string `json:"phase"`
Run func() error `json:"-"`
}
StartupTask is a one-shot job run before the HTTP listener binds. Migrations are the canonical case. The Run function executes inside the app process; the orchestration platform doesn't see Run, only the declared name + phase, so it can decide whether to gate the rollout on success ("don't promote until startup tasks pass").
type TLSBlock ¶ added in v0.47.0
type TLSBlock struct {
// Domains is the whitelist of hostnames the certificate manager
// is allowed to issue for. The tls extension passes these to
// autocert.HostWhitelist; a request for any other hostname is
// rejected before any ACME round-trip.
Domains []string `json:"domains,omitempty" toml:"domains,omitempty"`
// Email is the ACME account contact. Required for production
// Let's Encrypt; expiry warnings land here ~20 days before a
// cert lapses if our renewal fails. Treat as oncall@.
Email string `json:"email,omitempty" toml:"email,omitempty"`
// CacheDir is the on-disk directory the manager uses for cert
// storage. Operator picks the path; the orchestration platform
// guarantees a persistent volume mounted there when relevant.
CacheDir string `json:"cacheDir,omitempty" toml:"cache_dir,omitempty"`
// Redirect controls whether the :80 listener 301-redirects every
// non-ACME request to https://. Pointer so "absent" (inherit
// extension default) is distinguishable from explicit false.
Redirect *bool `json:"redirect,omitempty" toml:"redirect,omitempty"`
// Staging routes ACME requests to Let's Encrypt's staging
// directory. Use during development / preview environments to
// avoid burning production quota.
Staging bool `json:"staging,omitempty" toml:"staging,omitempty"`
// Disabled, when true, tells the tls extension to no-op for this
// environment. Useful when running behind a cloud LB that
// already terminates TLS, or when the deploy target is a dev
// laptop where binding :443 is not possible.
Disabled bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
TLSBlock declares the public-internet TLS configuration the binary wants the platform (or the in-process tls extension) to provide. Surfaced as a structured block instead of a soup of env vars so:
- lint/doctor can reason about it (wildcard rejection, missing email, conflicting cache config)
- environment_overrides can replace the whole domain list cleanly (production gets app.example.com; staging gets staging.app...)
- the dashboard's TLS tab can show "this is the declared shape" alongside the runtime status
Empty/nil means the binary does not request platform-managed TLS. The in-process tls extension may still be wired separately.
type TLSPatch ¶ added in v0.47.0
type TLSPatch struct {
// Domains: nil → inherit base; non-nil → fully replace. An
// explicit empty list ([]) means "no domains in this env", which
// is equivalent to setting Disabled=true.
Domains []string `json:"domains,omitempty" toml:"domains,omitempty"`
Email *string `json:"email,omitempty" toml:"email,omitempty"`
CacheDir *string `json:"cacheDir,omitempty" toml:"cache_dir,omitempty"`
Redirect *bool `json:"redirect,omitempty" toml:"redirect,omitempty"`
Staging *bool `json:"staging,omitempty" toml:"staging,omitempty"`
Disabled *bool `json:"disabled,omitempty" toml:"disabled,omitempty"`
}
TLSPatch is the override-adjustable subset of TLSBlock applied per-environment. Pointer fields let "absent" mean inherit; non-nil pointers replace. Domains uses a slice (not pointer) and follows list-replace semantics — production and staging typically declare fully different domain sets, so a deep-merge would only confuse.
type Volume ¶
Volume describes a path inside the container that must persist. Shared=true tells orchestration this volume must be mounted from a shared backing store when the app is scaled horizontally (e.g. local uploads dir read by every replica). Single-replica apps can ignore the flag.
type VolumeProvider ¶
type VolumeProvider interface {
NexusVolumes() []Volume
}
VolumeProvider is implemented by anything that needs a writable path. Most apps won't implement this — they'll register volumes directly via app.UseVolume(...) once that hook lands.
type WorkerSummary ¶
type WorkerSummary struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
WorkerSummary, CronSummary, EndpointSummary mirror what /__nexus/workers, /__nexus/crons, /__nexus/endpoints already return — flattened to the minimum a deployer needs (no health, no history, no per-call stats). The framework fills these from the registry; apps don't construct them directly.