Skip to content

HTTP API: environment

What this checkout is right now: the project and its version, the repository and its layer, version control, the toolchains it declares, what the layer holds, the workflows a person can run, the provider projections and the local services — one typed snapshot, with a provenance entry for every value in it. The direnv banner, the Cockpit's overview and this route are renderings of the same value.

Rendered from docs/generated/openapi.json, which the executable generates from its capability registry and CI refuses stale. The raw document is at /openapi.json; a running server serves the same document at its own /openapi.json and Swagger UI at /swagger.

environment

What this checkout is right now: the project and its version, the repository and its layer, version control, the toolchains it declares, what the layer holds, the workflows a person can run, the provider projections and the local services — one typed snapshot, with a provenance entry for every value in it. The direnv banner, the Cockpit's overview and this route are renderings of the same value.

GET /api/v1/environment environment.status

The repository environment

One snapshot of this checkout: project identity, repository identity, version control, declared toolchains, what the layer holds counted per kind, the workflows the runner describes, the provider projections against the policy that renders them, and the local services with the address a running server published. Every value carries where it came from.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_environment
  • MCP resource {"name": "environment", "uri": "majordomus://environment"}

Query parameters

nametyperequireddescriptionexamples
probe_services boolean no Contact the local address a running server published, to say whether it answers. Off by default: a served request should not open a socket to another server on behalf of its caller, and the caller usually is that server. default: false

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/environment?probe_services=false"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 RepositoryEnvironment The repository environment: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/environment/explain environment.explain

Where an environment value came from

The provenance of the snapshot: for each field, what decided it — a compile-time constant, a file, a command, or the cache — which resolver read it, and how far it can be trusted. Narrow it to one field, or to a prefix, by name.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_environment_explain

Query parameters

nametyperequireddescriptionexamples
field string no The field in dotted form (`services.url`, `capabilities.objects`); every field when absent. one-field: "project.version"

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/environment/explain?field=project.version"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 EnvironmentProvenance Where an environment value came from: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.
GET /api/v1/environment/preflight environment.preflight

Whether Majordomus is in force here, and what proves it

One verdict per claim about this checkout — git; the episode, its briefing, the task and the handover; the policy, the rule corpus and the ADRs; the shared server and the MCP, API and Cockpit surfaces it serves; the peer board; recorded test runs, rule enforcement, provider projections, generated documentation and the deployment — each `verified`, `active`, `fresh`, `stale`, `degraded`, `unavailable`, `failed`, `unknown` or `not_applicable`, with the evidence it rests on. A verdict that asserts something is in force cannot be produced without evidence. The same value the command line prints and the entry banner summarises.

  • query
  • behaviorally_verified
  • benchmark required
  • cache disabled
  • MCP tool majordomus_preflight
  • MCP resource {"name": "preflight", "uri": "majordomus://environment/preflight"}

Query parameters

nametyperequireddescriptionexamples
probe boolean no Ask the loopback address a server of this checkout published, and its peer board. Off by default: a served request answers from what this process can see, and when this process holds the checkout's lease that is itself the evidence. sealed: false

Try it

The port is whatever the server logged; 8741 is the one it asks for first. The values are the operation's first example.

curl -s "http://127.0.0.1:8741/api/v1/environment/preflight?probe=false"

Tested by apps/majordomus-cli/tests/http_serve.rs, which replays the route with these values against a server on a real socket.

Responses

statusbodymeaning
200 Preflight Whether Majordomus is in force here, and what proves it: the result.
400 ErrorBody invalid_input: The input does not fit the schema: a parameter of the wrong type, an unknown one, or a value the capability rejects.
404 ErrorBody not_found: The input names something the repository does not hold.
500 ErrorBody internal: The capability failed for a reason of its own; the message names it.
default ErrorBody Any other failure the transport reports: 405 method_not_allowed for another method on this path, 400 invalid_input for a body that is not a JSON object.

Schemas

The components only this tag's operations use. Each row is the type's own field, its doc comment the description; a type another page owns links there.

Confidence

"exact" | "cached" | "unknown"
How far a resolved value can be trusted.
  • exactRead from its canonical source during this resolution.
  • cachedRead from a cache whose fingerprint still matches its inputs.
  • unknownNot resolved.

Used by FieldSource

EnvironmentProvenance

object
Where the values of one snapshot came from.
propertytyperequireddescription
resolution Resolution → Resolution yes How the snapshot behind these entries was resolved.
fields array of FieldSource → FieldSource yes One entry per field, in the order the resolver produced them.

Used by environment.explain

FieldSource

object
Where one field of the snapshot came from. This is what makes an inferred system debuggable: every fact can name the thing that decided it, so "why does it say that" is answered by the tool rather than by reading its source.
propertytyperequireddescription
field string yes The field, in dotted form: services.cockpit.url, vcs.branch.
value string | null no The value as it appears in the snapshot, rendered for a person; absent for a field that resolved to nothing.
source string yes What decided it: a file, a command, a compile-time constant, the cache.
resolver string yes The part of this executable that read it.
confidence Confidence → Confidence yes How certain the value is.

Used by EnvironmentProvenance RepositoryEnvironment

GitWorkingTree

object
The state of one work tree, as porcelain v2 reports it.
propertytyperequireddescription
head string | null no The commit HEAD names, or None in a repository with no commits yet.
branch string | null no The branch, or None when HEAD is detached or unborn.
detached boolean yes Whether HEAD names a commit directly rather than a branch.
upstream string | null no The upstream the branch tracks, when it tracks one.
ahead integer | null no Commits this branch has that its upstream does not; None without an upstream.
behind integer | null no Commits the upstream has that this branch does not; None without an upstream.
staged integer (uint32) yes Tracked files with staged changes.
modified integer (uint32) yes Tracked files changed in the work tree and not staged.
untracked integer (uint32) yes Files git does not track and is not ignoring.
conflicted integer (uint32) yes Files with an unresolved merge.
clean boolean yes Whether nothing at all is staged, modified, untracked or conflicted.
changed_paths array of string no Every path that is not clean, repository-relative, in git's order. Used to decide whether the cached tier is still valid, and shown to nobody.

Used by VcsState

KindCount

object
How many valid objects of one kind the layer holds.
propertytyperequireddescription
kind string yes The kind, as share/kinds.yaml declares it.
count integer (uint) yes How many objects of it the index holds.

Used by LayerSummary

LayerSummary

object
What the layer holds, counted per kind, plus the registry the executable composes. Every number here comes from the index — the same objects every other surface serves — and never from counting files that match a pattern: a file that does not parse is not a rule, and a count that says otherwise is a lie a person acts on.
propertytyperequireddescription
state TierState → TierState yes Where these numbers came from.
kinds array of KindCount → KindCount no One entry per kind present, sorted by kind.
objects integer | null no How many objects the index holds.
capabilities integer | null no How many capabilities the registry holds.
invalid integer | null no How many files the layer declared that did not become objects.
refused array of Diagnostic → Diagnostic no Which ones, and what decided it: the index's own error diagnostics, each naming the file, the code and the constraint it failed. A count says something is wrong; only this says what and where, and a reader that has the count and not the list has to go looking through a log for the answer the index already produced.
degraded boolean | null no Whether the layer read cleanly.

Used by RepositoryEnvironment

Preflight

object
Whether Majordomus is in force in this checkout, claim by claim, with the evidence.

use majordomus_cli::environment::preflight::{derive, Observations, Preflight};
let p = derive(&Observations::empty("demo", 0));
assert_eq!(p.schema, Preflight::schema_id());
assert_eq!(p.verdicts().len(), p.sections.iter().map(|s| s.checks.len()).sum::<usize>());
propertytyperequireddescription
schema string yes majordomus/preflight/v1.
generated_at string yes When the observations were taken, RFC 3339 in UTC.
resolution Resolution → Resolution yes How the environment behind it was resolved.
repository string yes The repository's name.
branch string | null no The branch, when git named one.
head string | null no The commit HEAD names.
sections array of PreflightSection → PreflightSection yes The groups of checks.
attention array of string yes The ids of every check that needs a look, most urgent first.

Used by environment.preflight

PreflightCheck

object
One claim, its verdict, and what the verdict rests on.

use majordomus_cli::environment::preflight::{Check, Evidence, Verdict};

// a claim of something in force without evidence is not accepted as one
let bare = Check::new("integration.mcp", "MCP", Verdict::Verified, "healthy", vec![]);
assert_eq!(bare.verdict, Verdict::Unknown);
assert!(bare.summary.contains("no evidence"));

let backed = Check::new(
    "integration.mcp", "MCP", Verdict::Verified, "served",
    vec![Evidence::new("GET /", "surface mcp ready")],
);
assert_eq!(backed.verdict, Verdict::Verified);
propertytyperequireddescription
id string yes A stable dotted id, <section>.<claim>.
title string yes The short name a person reads.
verdict PreflightVerdict → PreflightVerdict yes What the repository can show.
summary string yes The verdict in one sentence, with the numbers that decided it.
evidence array of PreflightEvidence → PreflightEvidence no Everything the verdict rests on. Never empty for a verdict that proves something.
next string | null no The command that shows more, or that changes the verdict.

Used by PreflightSection

PreflightEvidence

object
One thing a verdict rests on: where it was read and what was seen there.

use majordomus_cli::environment::preflight::Evidence;
let e = Evidence::new(".ai/repo/evidence/ledger.json", "6 recorded runs");
assert_eq!(e.source, ".ai/repo/evidence/ledger.json");
propertytyperequireddescription
source string yes The file, command, route or capability that was read.
observed string yes What it said, in one phrase.

Used by PreflightCheck

PreflightSection

object
A group of checks: repository, session, governance, integration, verification.

use majordomus_cli::environment::preflight::{derive, Observations, Section};
let p = derive(&Observations::empty("demo", 0));
let ids: Vec<&str> = p.sections.iter().map(|s| s.id.as_str()).collect();
assert_eq!(ids, ["repository", "session", "governance", "integration", "verification"]);
// every check in a section carries the section's id as its prefix
let session: &Section = &p.sections[1];
assert!(session.checks.iter().all(|c| c.id.starts_with("session.")));
propertytyperequireddescription
id string yes A stable id, the prefix of every check in it.
title string yes The heading a person reads.
checks array of PreflightCheck → PreflightCheck yes The checks, in the order they are rendered.

Used by Preflight

PreflightVerdict

"verified" | "active" | "fresh" | "stale" | "degraded" | "unavailable" | "failed" | "unknown" | "not_applicable"
What the repository can show about one claim.

Nine words, not a boolean, because the actions they call for differ: stale is re-run the proof, unavailable is nothing is there, unknown is nobody looked, and not_applicable is there is nothing to look for.

use majordomus_cli::environment::preflight::Verdict;
assert!(Verdict::Verified.proves() && Verdict::Active.proves() && Verdict::Fresh.proves());
assert!(!Verdict::Stale.proves() && !Verdict::NotApplicable.proves());
assert_eq!(Verdict::NotApplicable.as_str(), "not_applicable");
// the most urgent first: a failure outranks a stale proof, which outranks an absence
assert!(Verdict::Failed.urgency() < Verdict::Stale.urgency());
assert_eq!(Verdict::Verified.urgency(), None);
  • verifiedProven against this tree by a recorded result or a direct answer.
  • activeIn force now, observed directly, with nothing recorded to verify.
  • freshJudged recent enough to act on, against a declared threshold or the current commit.
  • staleEvidence exists, but it is about another commit, tree or moment.
  • degradedIn force, but in a form that is not the one this executable would serve.
  • unavailableNothing is there to observe.
  • failedObserved, and it did not hold.
  • unknownNobody looked, or the looking did not finish.
  • not_applicableThere is nothing here to judge.

Used by PreflightCheck

ProjectIdentity

object
What the project is, from the crate manifest and the prose written once in crate::about. Every field here is a compile-time constant of this executable: there is no file to read, nothing to parse, and nothing that can disagree with the binary that answers.
propertytyperequireddescription
name string yes The product name.
version string yes The version of this executable, from the crate manifest.
summary string yes One sentence: what this is.
license string yes The SPDX licence identifier.
repository string yes The source repository.
target string yes The Rust target triple this executable was built for.
profile string yes The cargo profile it was built with.
commit string yes The commit it was built from, or unknown outside a work tree.

Used by RepositoryEnvironment

ProjectionState

"current" | "stale" | "absent" | "unknown"
Where one provider projection stands against its policy.
  • currentThe file matches the rendering byte for byte.
  • staleThe file differs: it was hand-edited, or the policy moved under it.
  • absentThe file is not there.
  • unknownIt could not be rendered, so nothing can be said.

Used by ProviderState

ProviderState

object
One provider projection the policy declares, and whether the file on disk still matches what the policy renders.
propertytyperequireddescription
id string yes The provider id, as the policy names it: agents, claude-code.
target string yes The repository-relative file it renders to.
state ProjectionState → ProjectionState yes Whether the file matches what the policy renders now.
always_loaded boolean yes Whether the provider loads this file into every context.

Used by RepositoryEnvironment

ReasoningSummary

object
Reasoning as the environment reports it: a summary of reasoning.advisors, from the same derivation, never a provider list of its own.

use majordomus_cli::environment::ReasoningSummary;

let summary: ReasoningSummary = serde_json::from_value(serde_json::json!({
    "operational": true, "mode": "offline", "available": ["local"], "unavailable": 2,
})).unwrap();
assert!(summary.operational);
assert_eq!(summary.available, ["local"]);
assert_eq!(serde_json::to_value(&summary).unwrap()["unavailable"], 2);
propertytyperequireddescription
operational boolean yes Reasoning works with no advisor at all; always true.
mode string yes The mode in force: offline, ci, fast, standard, strict.
available array of string yes The advisors that may be asked now, in preference order.
unavailable integer (uint) yes How many declared advisors may not, each optional.

Used by RepositoryEnvironment

RepositoryEnvironment

object
The snapshot: what this checkout is, right now.

Every surface that reports any of this renders this value. Collections are in a documented, stable order — kinds and workflows by name, services and providers in the order their canonical source declares them, diagnostics in the order they were found — so that two snapshots of the same repository serialise identically apart from RepositoryEnvironment::generated_at.

propertytyperequireddescription
schema string yes The contract this document follows, majordomus/repository-environment/v1.
generated_at string yes When it was taken, RFC 3339 in UTC.
resolution Resolution → Resolution yes How completely it was resolved.
project ProjectIdentity → ProjectIdentity yes What the project is.
repository RepositoryIdentity → RepositoryIdentity yes What the checkout is.
vcs VcsState → VcsState yes What version control says.
toolchains array of ToolchainState → ToolchainState yes The toolchains the repository declares, sorted by id.
layer LayerSummary → LayerSummary yes What the layer holds.
workflows WorkflowCatalogue → WorkflowCatalogue yes The workflows a person can run.
providers array of ProviderState → ProviderState yes The provider projections the policy declares, in the policy's order.
services array of ServiceState → ServiceState yes The local services, in the order the service table declares them.
reasoning ReasoningSummary | null → ReasoningSummary no Reasoning (ADR 0098): operational whatever the advisors, the mode in force, and how many optional advisors can be asked. Absent when the distribution declares none.
diagnostics array of Diagnostic → Diagnostic yes Everything that went wrong or is worth knowing, in the order it was found.
provenance array of FieldSource → FieldSource yes Where every field came from.

Used by environment.status

RepositoryIdentity

object
The checkout this snapshot is of.
propertytyperequireddescription
name string yes The name a person calls it: the base name of the root directory.
root string yes The root, absolute. The same value repository.info reports.
layer_schema string yes The manifest's schema, ai-repository/v1.
sections map of string yes Section name to repository-relative path, as the manifest declares them.
local_path string yes The checkout-local half of the layer, repository-relative. Never tracked, and where anything this checkout alone knows — the server's lease, this snapshot's cache — is kept.
linked_worktree boolean yes Whether this checkout is a linked work tree rather than the main one.

Used by RepositoryEnvironment

Resolution

"fast" | "full"
How completely a snapshot was resolved. Carried in the snapshot because a consumer must be able to tell "there is no server running" from "nobody looked".
  • fastOnly what is cheap enough for a shell prompt; the rest from the cache.
  • fullEverything, including the index. Writes the cache.

Used by EnvironmentProvenance Preflight RepositoryEnvironment

ServiceAvailability

"available" | "not_running" | "unknown"
Whether a service answers. Unknown is a real answer and never a disguised no: a probe that timed out and a port that refused the connection lead a reader to different actions.
  • availableThe address accepted a connection.
  • not_runningNo server holds the repository's lease, or the address refused the connection.
  • unknownThe probe did not finish in its budget, or this resolution did not probe.

Used by ServiceState

ServiceState

object
One local service of this repository: what it is, where it is, and whether anything answers there. The path is the one the router serves; the URL exists only while a server does.
propertytyperequireddescription
id string yes A stable id, [a-z][a-z0-9-]*.
title string yes The short name a person reads.
path string yes The absolute path the router serves it under.
url string | null no The full URL, when a server is running and published its address.
availability ServiceAvailability → ServiceAvailability yes Whether anything answers there.

Used by RepositoryEnvironment

TierState

"resolved" | "cached" | "unavailable"
Where a tier of the snapshot came from. A tier the active resolution cannot reach is TierState::Unavailable with the reason, and its values are absent rather than zero.
  • resolvedRead from its canonical source during this resolution.
  • cachedTaken from the cache written by an earlier full resolution.
  • unavailableNeither available; the values are absent and the reason is a diagnostic.

Used by LayerSummary WorkflowCatalogue

ToolchainAvailability

"installed" | "missing" | "unknown" | "mismatch"
Whether a declared toolchain is usable here.
  • installedInstalled, and its version answered.
  • missingThe executable is not on the path.
  • unknownNobody asked: this resolution may not run a subprocess for it, and no cache held it.
  • mismatchInstalled and answering, and its version does not satisfy the one the repository declares. Reported as itself rather than as installed, because a binary that is on the path and answers the wrong version is the case a person otherwise debugs for an hour (ADR 0064). Decided only when both versions are plain dotted numbers.

Used by ToolchainState

ToolchainState

object
A toolchain the repository declares, and what is installed for it. The three are kept apart on purpose: a repository can declare a version nobody has, and a machine can have a version no repository asked for, and reporting either as the other is how a version mismatch stays invisible for a week.
propertytyperequireddescription
id string yes A stable id, [a-z][a-z0-9-]*: rust, node.
title string yes The short name a person reads.
declared string | null no The version the repository declares, when it declares one.
declared_by string yes The repository-relative file that declares it.
installed string | null no The version installed here, when it could be asked and the answer is not stale.
availability ToolchainAvailability → ToolchainAvailability yes Where the installed version stands.

Used by RepositoryEnvironment

VcsState

one of 2 variants
What version control says about the checkout, or why it could not be asked.
variant git answered.
state"git"yes
variant git could not be asked, or this is not a work tree.
reasonstringyesWhat went wrong.
state"unavailable"yes

Used by RepositoryEnvironment

WorkflowCatalogue

object
The workflows a person can run here, as the workflow runner itself describes them.
propertytyperequireddescription
state TierState → TierState yes Where these came from.
source string | null no The command that produced them, when one did.
workflows array of WorkflowDescriptor → WorkflowDescriptor no Every public workflow, sorted by name.
entrypoints array of WorkflowEntrypoint → WorkflowEntrypoint no The entry point of each group, in the order the groups are declared: what a person new to the repository runs first. Derived, never listed anywhere.

Used by RepositoryEnvironment

WorkflowDescriptor

object
One workflow: a recipe of the repository's justfile, as just describes it.
propertytyperequireddescription
name string yes The name a person types after just.
namespace string | null no The module path, for a recipe in an imported module; empty at the root.
description string | null no The recipe's doc comment, when it has one.
group string | null no The [group(...)] it belongs to, when it declares one.
parameters array of WorkflowParameter → WorkflowParameter no Its parameters, in declaration order.
dependencies array of string no The recipes it runs first, in order.
confirm boolean yes Whether it asks before it runs ([confirm]).

Used by WorkflowCatalogue

WorkflowEntrypoint

object
The entry point of one group of workflows: what the group is, and the one workflow that stands for it.
propertytyperequireddescription
group string yes The group, as the justfile declares it.
workflow string yes The workflow that stands for the group.
command string yes The command a person types.
description string | null no The workflow's description, when it has one.

Used by WorkflowCatalogue

WorkflowParameter

object
One parameter of a workflow.
propertytyperequireddescription
name string yes The name.
variadic boolean yes Whether it may be repeated (*args, +args).
required boolean yes Whether it must be given.

Used by WorkflowDescriptor

Searches the titles and descriptions of every page this site publishes.