openapi: 3.1.0 info: title: grlx CLI API description: | Local HTTP API served by `grlx serve`. Proxies requests to the grlx farmer over NATS and serves the embedded web UI. This API is intended for local use only (default: localhost:7505). All endpoints under `/api/v1/` are JSON-over-HTTP proxies to the NATS-based farmer API. ## Response Envelope Most endpoints that proxy through NATS return responses wrapped in a standard envelope: ```json {"result": } ``` On error: ```json {"error": "message describing the problem"} ``` The schemas below describe the `result` payload for each endpoint. version: 0.2.0 license: name: Apache-2.0 url: https://www.apache.org/licenses/LICENSE-2.0 contact: name: grlx url: https://github.com/gogrlx/grlx servers: - url: http://localhost:7505 description: Default local grlx serve address paths: /api/v1/health: get: operationId: getHealth summary: Health check description: Returns a simple health status indicating the CLI serve process is running. tags: - health responses: "200": description: Healthy content: application/json: schema: $ref: "#/components/schemas/HealthResponse" /api/v1/version: get: operationId: getVersion summary: CLI and farmer version info description: Returns version information for both the CLI and the connected farmer. tags: - version responses: "200": description: Version information content: application/json: schema: $ref: "#/components/schemas/CombinedVersion" /api/v1/sprouts: get: operationId: listSprouts summary: List all sprouts description: | Returns a list of all sprouts known to the farmer, including key state and connectivity status. RBAC scope filtering is applied when the user does not have global view access. tags: - sprouts responses: "200": description: List of sprouts content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: sprouts: - id: web-01 key_state: accepted connected: true nkey: SUAM... - id: db-01 key_state: unaccepted connected: false "502": $ref: "#/components/responses/NATSError" /api/v1/sprouts/{id}: get: operationId: getSprout summary: Get sprout details description: Returns details for a specific sprout by ID, including key state, NKey, and connectivity. tags: - sprouts parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Sprout details content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: id: web-01 key_state: accepted connected: true nkey: SUAM... "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/jobs: get: operationId: listJobs summary: List all jobs description: | Returns a list of all jobs tracked by the farmer. Supports optional limit and user-based filtering. RBAC scope filtering restricts results to sprouts the user can view. tags: - jobs responses: "200": description: List of jobs content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: - jid: "20260321-103000-abc123" sprout_id: web-01 status: completed started_at: "2026-03-21T10:30:00Z" duration: 5000000000 succeeded: 3 failed: 0 skipped: 0 total: 3 invoked_by: PUBKEY123 "502": $ref: "#/components/responses/NATSError" /api/v1/jobs/{jid}: get: operationId: getJob summary: Get job details description: Returns details for a specific job by JID. tags: - jobs parameters: - $ref: "#/components/parameters/JobID" responses: "200": description: Job details content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" delete: operationId: cancelJob summary: Cancel a job description: | Cancels a running or pending job by JID. Publishes a cancel request to the target sprout over NATS. RBAC scope checking ensures the caller has `job_admin` permission for the job's sprout. tags: - jobs parameters: - $ref: "#/components/parameters/JobID" responses: "200": description: Job cancel request published content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: jid: "20260321-103000-abc123" sprout: web-01 message: cancel request published "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/jobs/sprout/{id}: get: operationId: getJobsForSprout summary: List jobs for a sprout description: Returns all jobs associated with a specific sprout. tags: - jobs parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Jobs for the sprout content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/cook: post: operationId: cook summary: Start a cook operation description: | Triggers a recipe cook on one or more sprouts. The request body contains a `TargetedAction` with a `CmdCook` action payload specifying the recipe path, target sprouts/cohorts, and options like test mode and timeout. tags: - cook requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/TargetedAction" examples: cook: value: target: - id: web-01 - id: web-02 action: recipe: base.webserver test: false async: false timeout: 300000000000 responses: "200": description: Cook initiated content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/props/{id}: get: operationId: getProps summary: Get all props for a sprout description: Returns all property key-value pairs for a specific sprout. tags: - props parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Sprout props as key-value map content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: os: linux role: webserver datacenter: us-east-1 "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/props/{id}/{key}: get: operationId: getPropKey summary: Get a specific prop value description: Returns the value of a single property key for a sprout. tags: - props parameters: - $ref: "#/components/parameters/SproutID" - $ref: "#/components/parameters/PropKey" responses: "200": description: Property value content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: sprout_id: web-01 name: role value: webserver "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" put: operationId: setPropKey summary: Set a prop value description: Sets or updates a single property key for a sprout. tags: - props parameters: - $ref: "#/components/parameters/SproutID" - $ref: "#/components/parameters/PropKey" requestBody: required: true content: application/json: schema: description: The value to set for this property key. type: object properties: value: type: string description: The property value to set responses: "200": description: Property set content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: success: true "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" delete: operationId: deletePropKey summary: Delete a prop description: Removes a single property key from a sprout. tags: - props parameters: - $ref: "#/components/parameters/SproutID" - $ref: "#/components/parameters/PropKey" responses: "200": description: Property deleted content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: success: true "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/cohorts: get: operationId: listCohorts summary: List all cohorts description: Returns all cohort definitions with name and type. tags: - cohorts responses: "200": description: List of cohorts content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: cohorts: - name: web-servers type: static - name: us-east type: dynamic - name: production type: compound "502": $ref: "#/components/responses/NATSError" /api/v1/cohorts/resolve: post: operationId: resolveCohorts summary: Resolve cohort membership description: | Resolves a cohort expression against current sprout state and returns the list of matching sprout IDs. tags: - cohorts requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CohortResolveRequest" responses: "200": description: Resolved sprout list content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: name: web-servers sprouts: - web-01 - web-02 - web-03 "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/cohorts/{name}: get: operationId: getCohort summary: Get cohort details description: | Returns the full definition and resolved membership of a named cohort, including type, membership rules, and currently resolved sprout IDs. tags: - cohorts parameters: - name: name in: path required: true description: Cohort name schema: type: string responses: "200": description: Cohort detail content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: name: web-servers type: static members: - web-01 - web-02 resolved: - web-01 - web-02 count: 2 "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/cohorts/refresh: post: operationId: refreshCohorts summary: Refresh cohort membership cache description: | Re-evaluates cohort membership against current sprout state and updates the cached membership. If a cohort name is provided in the request body, only that cohort is refreshed. Otherwise all cohorts are refreshed. tags: - cohorts requestBody: required: false content: application/json: schema: $ref: "#/components/schemas/CohortRefreshRequest" responses: "200": description: Refresh results content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: refreshed: - name: web-servers members: - web-01 - web-02 lastRefreshed: "2026-03-21T10:00:00Z" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/cohorts/validate: get: operationId: validateCohorts summary: Validate cohort references description: | Checks that all compound cohort operands reference existing cohorts, no circular references exist, and nesting depth does not exceed the maximum. Useful for verifying configuration correctness. tags: - cohorts responses: "200": description: Validation result content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: valid: value: result: valid: true cohorts: 3 invalid: value: result: valid: false errors: - "cohort 'missing' referenced by 'compound-1' does not exist" cohorts: 3 "502": $ref: "#/components/responses/NATSError" /api/v1/keys: get: operationId: listKeys summary: List all sprout keys description: Returns all sprout PKI keys grouped by status (accepted, unaccepted, denied, rejected). tags: - keys responses: "200": description: Key listing content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: accepted: sprouts: - id: web-01 - id: db-01 unaccepted: sprouts: - id: new-sprout denied: sprouts: [] rejected: sprouts: [] "502": $ref: "#/components/responses/NATSError" /api/v1/keys/{id}: delete: operationId: deleteKey summary: Delete a sprout key description: Permanently removes a sprout's key from the farmer PKI store. tags: - keys parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Key deleted content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/keys/{id}/accept: post: operationId: acceptKey summary: Accept a sprout key description: Moves a pending sprout key to the accepted state. tags: - keys parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Key accepted content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/keys/{id}/reject: post: operationId: rejectKey summary: Reject a sprout key description: Rejects a pending sprout key. tags: - keys parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Key rejected content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/keys/{id}/deny: post: operationId: denyKey summary: Deny a sprout key description: Denies a sprout key, preventing future connections. tags: - keys parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Key denied content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/keys/{id}/unaccept: post: operationId: unacceptKey summary: Unaccept a sprout key description: Moves an accepted sprout key back to pending state. tags: - keys parameters: - $ref: "#/components/parameters/SproutID" responses: "200": description: Key unaccepted content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/auth/whoami: get: operationId: getWhoAmI summary: Current user identity description: Returns the identity of the currently authenticated CLI user. tags: - auth responses: "200": description: Current user info content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: pubkey: PUBKEY123... role: admin "502": $ref: "#/components/responses/NATSError" /api/v1/auth/explain: get: operationId: explainAuth summary: Explain current user permissions description: | Returns the RBAC permissions breakdown for the currently authenticated user, including role name, admin status, allowed actions, and any policy warnings. tags: - auth responses: "200": description: Permission explanation content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: pubkey: PUBKEY123... role: operator isAdmin: false actions: - action: view scope: "*" - action: cook scope: "cohort:web-servers" - action: cmd scope: "cohort:web-servers" "502": $ref: "#/components/responses/NATSError" /api/v1/auth/users: get: operationId: listUsers summary: List all users description: | Returns all registered users in the farmer's auth system, along with all defined role definitions and their permission rules. tags: - auth responses: "200": description: User list with role definitions content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: users: PUBKEY123: admin PUBKEY456: operator roles: - name: admin rules: - action: admin scope: "*" - name: operator rules: - action: view scope: "*" - action: cook scope: "cohort:web-servers" "502": $ref: "#/components/responses/NATSError" post: operationId: addUser summary: Add a user description: | Register a new user with the given public key and role. Requires admin permissions. tags: - auth requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UserAddRequest" responses: "200": description: User added content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: success: true message: user PUBKEY789 added with role viewer "502": $ref: "#/components/responses/NATSError" /api/v1/auth/users/{pubkey}: delete: operationId: removeUser summary: Remove a user description: | Remove a registered user by public key. Requires admin permissions. tags: - auth parameters: - name: pubkey in: path required: true schema: type: string description: The public key of the user to remove responses: "200": description: User removed content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: success: true message: user PUBKEY789 removed "400": description: Missing pubkey parameter content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "502": $ref: "#/components/responses/NATSError" /api/v1/cmd: post: operationId: runCommand summary: Run ad-hoc command on sprouts description: | Execute a command on one or more targeted sprouts. The request body is a `TargetedAction` with a `CmdRun` action payload containing the command, arguments, working directory, optional runas user, environment variables, and timeout. tags: - cmd requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/TargetedAction" examples: cmdRun: value: target: - id: web-01 action: command: uptime args: [] timeout: 30000000000 responses: "200": description: Command results per sprout content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": description: Invalid request body content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "502": $ref: "#/components/responses/NATSError" /api/v1/test/ping: post: operationId: pingTest summary: Ping sprouts description: | Send a test ping to one or more targeted sprouts and collect their responses. Useful for checking sprout connectivity. tags: - test requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/TargetedAction" examples: ping: value: target: - id: web-01 - id: web-02 action: ping: true responses: "200": description: Ping results per sprout content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" "400": description: Invalid request body content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "502": $ref: "#/components/responses/NATSError" /api/v1/recipes: get: operationId: listRecipes summary: List all recipes description: | Returns a list of all recipes available on the farmer, including their dot-notation names, file paths, and sizes. tags: - recipes responses: "200": description: Recipe list content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: recipes: - name: base.webserver path: base/webserver.grlx size: 1234 - name: monitoring.prometheus path: monitoring/prometheus.grlx size: 2048 "502": $ref: "#/components/responses/NATSError" /api/v1/recipes/{id...}: get: operationId: getRecipe summary: Get a recipe by path description: | Returns the contents of a recipe file by its dot-notation name. Uses a wildcard path parameter to support nested paths (e.g., `base.webserver` or `base/webserver`). tags: - recipes parameters: - name: id in: path required: true description: Recipe name in dot-notation (e.g., `base.webserver`) or path form schema: type: string responses: "200": description: Recipe contents content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: name: base.webserver path: base/webserver.grlx content: | nginx: pkg.installed: - name: nginx service.running: - name: nginx - enable: true size: 1234 "400": $ref: "#/components/responses/BadRequest" "502": $ref: "#/components/responses/NATSError" /api/v1/audit/dates: get: operationId: listAuditDates summary: List available audit log dates description: | Returns the dates for which audit log entries exist, along with entry counts and file sizes. Sorted most recent first. tags: - audit responses: "200": description: Audit date list content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: - date: "2026-03-21" entry_count: 42 size_bytes: 8192 - date: "2026-03-20" entry_count: 28 size_bytes: 5120 "502": $ref: "#/components/responses/NATSError" /api/v1/audit: get: operationId: queryAuditLog summary: Query audit log description: | Queries the audit log with optional filtering. Returns entries sorted most recent first, capped by limit (default 100). tags: - audit parameters: - name: date in: query description: Filter by date (YYYY-MM-DD). Defaults to today. schema: type: string format: date - name: user in: query description: Filter by user pubkey (exact match) schema: type: string - name: action in: query description: Filter by action type (exact match) schema: type: string - name: limit in: query description: Maximum entries to return (default 100) schema: type: integer minimum: 1 - name: failed_only in: query description: Return only failed entries schema: type: boolean responses: "200": description: Audit log entries content: application/json: schema: $ref: "#/components/schemas/NATSEnvelope" examples: success: value: result: date: "2026-03-21" entries: - timestamp: "2026-03-21T10:30:00Z" pubkey: PUBKEY123 role: admin action: cook targets: - web-01 success: true - timestamp: "2026-03-21T10:25:00Z" pubkey: PUBKEY456 role: operator action: cmd.run targets: - db-01 success: false error: "access denied" total: 2 "502": $ref: "#/components/responses/NATSError" /api/v1/logs: get: operationId: getRecentLogs summary: Get recent log entries description: | Returns recent log entries from the in-memory buffer, optionally filtered by level and source. Entries are ordered chronologically. tags: - logs parameters: - name: level in: query description: Minimum log level filter schema: type: string enum: - debug - info - warn - error - name: source in: query description: Filter by log source type schema: type: string enum: - farmer - sprout - name: limit in: query description: Maximum number of entries to return (default 100) schema: type: integer minimum: 1 responses: "200": description: Recent log entries content: application/json: schema: type: object properties: logs: type: array items: $ref: "#/components/schemas/LogEntry" /api/v1/logs/stream: get: operationId: streamLogs summary: Stream log entries via WebSocket description: | Upgrades the connection to a WebSocket and streams log entries as newline-delimited JSON messages. Supports optional level and source query parameter filters. The server sends periodic ping frames to keep the connection alive. tags: - logs parameters: - name: level in: query description: Minimum log level filter schema: type: string enum: - debug - info - warn - error - name: source in: query description: Filter by log source type schema: type: string enum: - farmer - sprout responses: "101": description: WebSocket upgrade successful /api/v1/openapi.yaml: get: operationId: getOpenAPISpec summary: OpenAPI specification description: Returns this OpenAPI specification document. tags: - meta responses: "200": description: OpenAPI 3.1 YAML document content: application/x-yaml: schema: type: string components: parameters: SproutID: name: id in: path required: true description: Sprout identifier schema: type: string JobID: name: jid in: path required: true description: Job identifier schema: type: string PropKey: name: key in: path required: true description: Property key name schema: type: string schemas: HealthResponse: type: object required: - status properties: status: type: string enum: - ok example: ok Version: type: object properties: arch: type: string description: Build architecture example: amd64 compiler: type: string description: Go compiler version example: go1.24.1 git_commit: type: string description: Git commit hash example: abc1234 tag: type: string description: Release tag example: v0.3.0 CombinedVersion: type: object properties: cli: $ref: "#/components/schemas/Version" farmer: $ref: "#/components/schemas/Version" error: type: string description: Error message if farmer version could not be retrieved SproutInfo: type: object description: A sprout with its key state and connectivity status. required: - id - key_state - connected properties: id: type: string description: Sprout identifier example: web-01 key_state: type: string description: PKI key state enum: - accepted - unaccepted - denied - rejected - unknown example: accepted connected: type: boolean description: Whether the sprout responds to ping example: true nkey: type: string description: NKey public key (when available) JobSummary: type: object description: Overview of a job's execution. required: - jid - sprout_id - status - started_at properties: jid: type: string description: Job identifier example: "20260321-103000-abc123" sprout_id: type: string description: Target sprout ID example: web-01 status: type: string description: Current job status enum: - pending - running - completed - failed - partial example: completed steps: type: array description: Step-by-step completion details items: $ref: "#/components/schemas/StepCompletion" started_at: type: string format: date-time description: When the job started duration: type: integer description: Duration in nanoseconds succeeded: type: integer description: Number of successful steps failed: type: integer description: Number of failed steps skipped: type: integer description: Number of skipped steps total: type: integer description: Total number of steps invoked_by: type: string description: Public key of the user who invoked the job StepCompletion: type: object description: Completion status of a single recipe step. properties: id: type: string description: Step identifier completion_status: type: string description: Step outcome changes_made: type: boolean description: Whether the step made changes to the system changes: type: array items: type: string description: List of changes made started: type: string format: date-time description: When the step started duration: type: integer description: Step duration in nanoseconds CohortSummary: type: object description: Basic info about a named cohort. required: - name - type properties: name: type: string description: Cohort name example: web-servers type: $ref: "#/components/schemas/CohortType" CohortDetail: type: object description: Full definition of a cohort including resolved membership. required: - name - type - resolved - count properties: name: type: string description: Cohort name type: $ref: "#/components/schemas/CohortType" members: type: array items: type: string description: Explicit member list (static cohorts) match: $ref: "#/components/schemas/DynamicMatch" compound: $ref: "#/components/schemas/CompoundExpr" resolved: type: array items: type: string description: Currently resolved sprout IDs count: type: integer description: Number of resolved members CohortType: type: string description: How a cohort's membership is determined. enum: - static - dynamic - compound DynamicMatch: type: object description: Matches sprouts whose props satisfy a condition. properties: propName: type: string description: Property name to match against example: role propValue: type: string description: Required property value example: webserver CompoundExpr: type: object description: Boolean combination of named cohorts. required: - operator - operands properties: operator: type: string description: Boolean operator enum: - AND - OR - EXCEPT operands: type: array items: type: string description: Names of cohorts to combine CohortResolveRequest: type: object description: Request to resolve a cohort's membership. required: - name properties: name: type: string description: Cohort name to resolve example: web-servers CohortRefreshRequest: type: object description: | Optional request body for cohort refresh. If a name is provided, only that cohort's membership cache is refreshed. If omitted or empty, all cohorts are refreshed. properties: name: type: string description: Name of a specific cohort to refresh example: web-servers CohortRefreshResponse: type: object description: Results of a cohort refresh operation. required: - refreshed properties: refreshed: type: array items: $ref: "#/components/schemas/RefreshResult" RefreshResult: type: object description: Result of refreshing a single cohort's membership. required: - name - members - lastRefreshed properties: name: type: string description: Cohort name members: type: array items: type: string description: Resolved member sprout IDs lastRefreshed: type: string format: date-time description: When the membership was last evaluated CohortValidateResponse: type: object description: Result of validating all cohort references. required: - valid - cohorts properties: valid: type: boolean description: Whether all cohort references are valid errors: type: array items: type: string description: Validation error messages (only present when invalid) cohorts: type: integer description: Total number of cohorts checked RecipeInfo: type: object description: A recipe file in the listing. required: - name - path - size properties: name: type: string description: Dot-notation recipe name example: base.webserver path: type: string description: Relative file path from recipe root example: base/webserver.grlx size: type: integer format: int64 description: File size in bytes RecipeContent: type: object description: Full content of a recipe file. required: - name - path - content - size properties: name: type: string description: Dot-notation recipe name path: type: string description: Relative file path from recipe root content: type: string description: Raw recipe file content size: type: integer format: int64 description: File size in bytes KeysByType: type: object description: Sprout keys grouped by PKI state. properties: accepted: $ref: "#/components/schemas/KeySet" unaccepted: $ref: "#/components/schemas/KeySet" denied: $ref: "#/components/schemas/KeySet" rejected: $ref: "#/components/schemas/KeySet" KeySet: type: object properties: sprouts: type: array items: type: object properties: id: type: string description: Sprout identifier UserInfo: type: object description: A user's identity and role. required: - pubkey - role properties: pubkey: type: string description: User's public key role: type: string description: Assigned role name ExplainResponse: type: object description: RBAC permissions breakdown for a user. required: - pubkey - role - isAdmin - actions properties: pubkey: type: string description: User's public key role: type: string description: Assigned role name isAdmin: type: boolean description: Whether the user has admin access actions: type: array items: $ref: "#/components/schemas/ActionExplain" warnings: type: array items: $ref: "#/components/schemas/PolicyWarning" ActionExplain: type: object description: A single permitted action. required: - action - scope properties: action: $ref: "#/components/schemas/RBACAction" scope: type: string description: Scope expression (e.g., `*`, `cohort:web-servers`) example: "*" RBACAction: type: string description: RBAC action type. enum: - admin - view - cook - cmd - shell - job_admin - key_admin - user_admin - prop_admin PolicyWarning: type: object description: A policy misconfiguration warning. required: - kind - message properties: kind: type: string description: Warning category example: orphan_role_ref message: type: string description: Human-readable description RoleInfo: type: object description: A role and its permission rules. required: - name - rules properties: name: type: string description: Role name example: operator rules: type: array items: $ref: "#/components/schemas/Rule" Rule: type: object description: A single RBAC permission rule. required: - action properties: action: $ref: "#/components/schemas/RBACAction" scope: type: string description: Scope expression example: "cohort:web-servers" UsersListResponse: type: object description: All users and role definitions. required: - users - roles properties: users: type: object additionalProperties: type: string description: Map of pubkey to role name roles: type: array items: $ref: "#/components/schemas/RoleInfo" UserAddRequest: type: object description: Payload for adding a user. required: - pubkey - role properties: pubkey: type: string description: The user's public key role: type: string description: Role to assign (e.g., admin, operator, viewer) UserMutateResponse: type: object description: Response after user add/remove operations. required: - success properties: success: type: boolean message: type: string description: Human-readable result message AuditEntry: type: object description: A single audit log record. required: - timestamp - pubkey - role - action - success properties: timestamp: type: string format: date-time description: When the action occurred pubkey: type: string description: User who performed the action role: type: string description: User's role at time of action action: type: string description: Action type (e.g., cook, cmd.run, pki.accept) targets: type: array items: type: string description: Sprout IDs or other targets affected params: description: Action-specific parameters (raw JSON) success: type: boolean description: Whether the action succeeded error: type: string description: Error message if the action failed AuditQueryResult: type: object description: Result of an audit log query. required: - date - entries - total properties: date: type: string description: Date queried (YYYY-MM-DD) entries: type: array items: $ref: "#/components/schemas/AuditEntry" total: type: integer description: Total entries matching filters (before limit) AuditDateSummary: type: object description: Summary of a single audit log date. required: - date - entry_count - size_bytes properties: date: type: string description: Date (YYYY-MM-DD) entry_count: type: integer description: Number of entries for this date size_bytes: type: integer format: int64 description: Log file size in bytes TargetedAction: type: object description: | A targeted action request containing a list of sprout targets and an action payload. Used by cook, cmd.run, and test.ping endpoints. required: - target - action properties: target: type: array items: type: object required: - id properties: id: type: string description: Sprout identifier description: List of sprouts to target action: description: | Action-specific payload. For `cmd.run`, this is a CmdRun object. For `cook`, this is a CmdCook object. For `test.ping`, this is a PingPong object. CmdRun: type: object description: | Command execution parameters for ad-hoc command runs on sprouts. properties: command: type: string description: Command to execute example: uptime args: type: array items: type: string description: Command arguments path: type: string description: PATH override for command lookup cwd: type: string description: Working directory runas: type: string description: Execute as this user env: type: object additionalProperties: type: string description: Environment variables timeout: type: integer description: Timeout in nanoseconds stdout: type: string description: Standard output (in response) stderr: type: string description: Standard error (in response) duration: type: integer description: Execution duration in nanoseconds (in response) errcode: type: integer description: Exit code (in response) CmdCook: type: object description: Cook operation parameters. properties: recipe: type: string description: Recipe name in dot-notation example: base.webserver state: type: string description: >- Optional. Run only the named state (step ID) and the transitive closure of its requisite dependencies, instead of the whole recipe. example: install-nginx test: type: boolean description: Run in test (dry-run) mode default: false async: type: boolean description: Run asynchronously default: false env: type: string description: Environment name timeout: type: integer description: Timeout in nanoseconds jid: type: string description: Job ID (in response) PingPong: type: object description: Ping/pong connectivity test. properties: ping: type: boolean description: Set to true in request pong: type: boolean description: Set to true in response if sprout is alive LogEntry: type: object description: A single log entry from the in-memory buffer. required: - timestamp - level - source - message properties: timestamp: type: string format: date-time description: ISO 8601 timestamp example: "2026-03-21T07:30:00Z" level: type: string enum: - debug - info - warn - error description: Log severity level source: type: string enum: - farmer - sprout description: Origin of the log entry sourceId: type: string description: Identifier of the source (e.g., sprout ID) example: web-01 message: type: string description: Human-readable log message NATSEnvelope: type: object description: | Standard NATS proxy response envelope. On success, `result` contains the response data. On error, `error` contains a message string. properties: result: description: Response data (structure varies by endpoint — see endpoint examples) error: type: string description: Error message (present only on failure) ErrorResponse: type: object required: - error properties: error: type: string description: Error message example: missing id parameter responses: BadRequest: description: Bad request — missing or invalid parameters content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" NATSError: description: NATS communication error — farmer unreachable or returned an error content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" tags: - name: health description: Health check - name: version description: Version information - name: sprouts description: Sprout (managed node) management - name: jobs description: Job tracking and management - name: cook description: Recipe execution - name: props description: Sprout properties (key-value metadata) - name: cohorts description: Cohort (sprout group) management - name: keys description: PKI key management - name: auth description: Authentication and user management - name: recipes description: Recipe listing and retrieval - name: audit description: Audit log queries - name: logs description: Log streaming and retrieval - name: cmd description: Ad-hoc command execution - name: test description: Sprout connectivity testing - name: meta description: API metadata