Publish a cookbook and grant the team
A LEMC admin does not have to choose between handing out full control and doing every deployment personally. Give an author the narrow right to create cookbooks, let LEMC govern how their container enters the managed registry, then distribute the finished work as an app.
Grant authorship to the author. Grant execution to the user. Cookbook ACLs protect source and publishing; app ACLs protect who may run the installed computation.
The authority split
| Role | Minimum authority | What it can do |
|---|---|---|
| Account admin or owner | can_administer or account ownership | Create account users and set their account-level permissions. |
| Cookbook author | can_view_cookbooks + can_create_cookbooks | Create a cookbook, become its owner, edit its contract, publish managed images, and publish the cookbook. |
| App deployer | can_view_apps + can_create_apps, plus access to the published cookbook | Install the cookbook as an app and become that app's owner. |
| App owner or admin | App owner or admin ACL | Add members and choose individual, shared, or app-admin access. |
| App member | can_view_apps + app individual or shared ACL | Discover and run authorized actions without cookbook edit rights. |
can_create_cookbooks and can_create_apps are separate account permissions. Creating a cookbook makes the creator owner of that cookbook; it does not grant edit access to every cookbook in the account.
Web and CLI responsibilities
Account users, account permissions, cookbook and app records, and their ACLs can be managed from LEMC's web interface. The commands below make the complete workflow explicit and scriptable.
Container publishing intentionally stays developer-side: Docker
builds on the author's machine and lemcli publishes
through the LEMC registry front door. The LEMC server never needs
the author's Docker socket, a host Docker daemon, or a broad,
reusable registry credential to run recipes.
Example: let an agent draft a timed GCP server
Open Codex, Claude, Grok, Gemini, or another approved command-line coding agent in an empty cookbook checkout and feed it the operating contract. Require it to run lemcli --help plus focused cookbook, image, secret, and job subcommand help before writing anything; do not paste the GCP service-account JSON into its prompt. Claude Code accepts the same one-shot brief with claude -p '…'.
codex exec 'Run lemcli --help and focused subcommand help first.
Use the observed CLI and cookbook contracts—not remembered syntax—to build
a LEMC cookbook with Terraform containers and v1 callbacks.
Create two recipes:
1. create a GCP server now and destroy it in 24 hours
2. destroy the tracked server immediately
Reuse the same idempotent, ownership-checked destroy image for the create
recipe's before_run callback, its IN phase, and the immediate destroy recipe.
Validate ownership before destructive work. Preserve Terraform state,
logs, artifacts, and terminal proof. Declare
GCP_SERVICE_ACCOUNT_JSON by name only.'
The agent can draft cookbook.yaml, the Terraform module, the recipe container, ownership checks, tests, and the managed-image publication commands. A human reviews those files and supplies the protected key separately through lemcli cookbooks secrets set --file. The agent may inspect names-only secret status and submit the reviewed server action; LEMC resolves the value inside the job and returns only redacted events and proof.
Keep local checks secret-free when the coding agent has unrestricted access to the developer account. lemcli dev secrets set --file and lemcli dev run can inject a local value into Docker without placing it in an argument or ordinary output, but that permission boundary does not isolate the value from an agent running as the same OS user. Use the server-side job for the real credential test, or have a human run the reviewed local recipe outside the agent session.
cookbook:
name: GCP Server Lifecycle
canonical_name: gcp-server-lifecycle
environment:
secrets:
- GCP_SERVICE_ACCOUNT_JSON
pages:
- page: 1
name: Timed GCP server
recipes:
- recipe: Create a server
canonical_name: create-server
description: Create one server and tear it down after 24 hours.
callbacks:
before_run:
steps:
- step: 1
name: Destroy if present
image: gcp-server-destroy:dev
timeout: 10.minutes
now:
steps:
- step: 1
name: Apply the declared server
image: gcp-server-create:dev
timeout: 20.minutes
in:
duration: 24.hours
steps:
- step: 1
name: Destroy after TTL
image: gcp-server-destroy:dev
timeout: 10.minutes
- recipe: Destroy the server now
canonical_name: destroy-server-now
description: Verify & destroy server
now:
steps:
- step: 1
name: Destroy the tracked server
image: gcp-server-destroy:dev
timeout: 10.minutes
The create image reads project_id from the separately bound service-account JSON and carries reviewed defaults for zone, instance name, and machine type, so the recipe needs no form. One idempotent teardown image covers three boundaries: before_run removes a prior tracked server before replacement, IN 24.hours enforces the new server's TTL, and the second recipe exposes immediate teardown. No invented executable override is required; each image uses the command baked into its container. The readable local tags are publication inputs; lemcli pushes them through the configured LEMC server's registry front door and rewrites each binding to its immutable managed digest before team use.
1. Admin: create a bounded author
First create the teammate's account identity without putting a password in shell history.
read -rsp "Temporary author password: " AUTHOR_PASSWORD
printf '\n'
lemcli account users create \
--username cookbook-author \
--email author@example.com \
--password "$AUTHOR_PASSWORD" \
--format=json
unset AUTHOR_PASSWORD
export AUTHOR_ID="<returned-user-id>"
Open only the cookbook-authoring lane:
lemcli account users permission toggle "$AUTHOR_ID" \
--permission can_view_cookbooks --value true
lemcli account users permission toggle "$AUTHOR_ID" \
--permission can_create_cookbooks --value true
If the same teammate should install finished cookbooks as apps, add the two app permissions. Otherwise an admin or separate deployer can perform that step.
lemcli account users permission toggle "$AUTHOR_ID" \
--permission can_view_apps --value true
lemcli account users permission toggle "$AUTHOR_ID" \
--permission can_create_apps --value true
Do not grant can_administer merely so someone can publish their own cookbook. The create permission plus ownership of the new cookbook is sufficient.
2. Author: create the cookbook contract
Authenticate as the author, create the remote record, and keep the returned UUID.
lemcli cookbooks create \
--name "Release checks" \
--description "Repeatable production readiness checks" \
--format=json
export COOKBOOK_UUID="<returned-cookbook-uuid>"
Write the wiki-facing structure and recipe steps in cookbook.yaml. The local development image is deliberately mutable at this point; LEMC replaces it with a managed digest during publication.
cookbook:
name: Release checks
canonical_name: release-checks
pages:
- page: 1
name: Production readiness
recipes:
- recipe: release readiness
canonical_name: release-readiness
description: Check whether production is ready
now:
steps:
- step: 1
name: Check release readiness
image: acme/release-check:dev
timeout: 10.minutes
Validate first, then upload the contract:
lemcli cookbooks push "$COOKBOOK_UUID" \
--file cookbook.yaml --dry-run
lemcli cookbooks push "$COOKBOOK_UUID" \
--file cookbook.yaml
Start from a maintained source example
The LEMC source tree includes 10 credential-sanitized cookbook projects under examples/cookbooks/. Each project keeps its declaration-only cookbook.yaml, Docker build context, recipe source, and any reviewed Terraform or install assets together.
| Example family | Projects | Declared secret |
|---|---|---|
| AWS VM lifecycle | lemc-aws-terraform-vm | AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY |
| GCP VM lifecycle | lemc-gcp-terraform-debian, lemc-gcp-terraform-debian-docker-1h, lemc-gcp-terraform-vm, and lemc-gcp-terraform-vm-custom-ssh-port | GOOGLE_CREDENTIALS |
| GCP challenge hosts | lemc-gcp-challenge-web, lemc-gcp-challenge-wetty, lemc-hhc25-wetty, and lemc-ssq-wetty | GOOGLE_CREDENTIALS |
| Local challenge proof | lemc-hhc25-single-challenge-debian | None |
Validate a project without loading local state, then build its mutable development image:
cd examples/cookbooks
./scripts/validate.sh
lemcli dev load lemc-gcp-terraform-vm/cookbook.yaml \
--dry-run --format=json
cd lemc-gcp-terraform-vm
lemcli cookbooks images build lemc-gcp-terraform-vm:dev \
--context . --file Dockerfile
Before hosted use, create the remote cookbook, push the YAML, bind the declared value separately with lemcli cookbooks secrets set, and publish every local :dev image through lemcli cookbooks images push --uuid … --update-yaml. Verify the pulled cookbook contains LEMC-managed immutable digests before setting it published.
These are reviewed starting points, not pre-authorized infrastructure changes. Inspect Terraform plans, target projects or accounts, network exposure, teardown behavior, costs, and challenge content before binding credentials or running a recipe.
3. Author: publish the container through LEMC
From the directory containing the recipe's Dockerfile, build and publish in one managed flow:
lemcli cookbooks images push acme/release-check:dev \
--uuid "$COOKBOOK_UUID" \
--build \
--dockerfile Dockerfile \
--build-context . \
--target-suffix recipes/release-check \
--source-yaml-image acme/release-check:dev \
--update-yaml \
--format=json
That one command crosses a deliberate authority boundary:
lemclivalidates the local image and confirms the source image appears in the remote cookbook YAML.- LEMC issues a short-lived push token scoped to this cookbook's server-controlled registry namespace.
- Docker tags and pushes the local image through the LEMC registry front door.
- LEMC validates and promotes the manifest to an immutable digest while recording publisher and recipe-binding metadata.
- The CLI rewrites the remote YAML step to the managed
@sha256:…reference.
Verify the image policy and pull back the authoritative contract before publishing it:
lemcli cookbooks images status \
--uuid "$COOKBOOK_UUID" --format=json
lemcli cookbooks pull "$COOKBOOK_UUID" \
--out cookbook.published.yaml
lemcli cookbooks toggle "$COOKBOOK_UUID" \
--type published --value true --format=json
A hosted job runs the authorized managed digest. Recipe execution does not build, import, or remotely pull an arbitrary mutable image on demand.
4. Author: bind secret values separately
If a recipe needs a credential, declare only its name in cookbook YAML. A declaration is a reference, never a value or ciphertext:
cookbook:
environment:
secrets:
- RELEASE_API_TOKEN
Bind the value through the authenticated secret endpoint after the declaration exists. Prompting, standard input, and permission-checked files keep it out of command arguments:
lemcli cookbooks secrets set "$COOKBOOK_UUID" RELEASE_API_TOKEN
printf '%s\n' "$RELEASE_API_TOKEN" |
lemcli cookbooks secrets set "$COOKBOOK_UUID" RELEASE_API_TOKEN --stdin
lemcli cookbooks secrets status "$COOKBOOK_UUID" --format=json
On a configured LEMC server, the remote binding is versioned and encrypted at rest with AES-256-GCM. The cookbook itself is not an encrypted secret: its YAML keeps only the named reference, and list, status, audit, and API responses return metadata rather than the plaintext value.
An agent can author the declaration and drive lemcli while a human supplies the value through a separate prompt, stdin stream, or protected file. The agent does not need the credential in its context.
5. Deployer: install the app
A caller with app-creation authority and access to the published cookbook installs the user-facing app:
lemcli apps create \
--cookbook-uuid "$COOKBOOK_UUID" \
--name "Release operations" \
--description "Team-owned production readiness" \
--format=json
export APP_UUID="<returned-app-uuid>"
The app receives a snapshot of the published cookbook and its validated image bindings. Its creator becomes app owner. A later cookbook change does not silently change installed behavior; an app owner or admin refreshes it deliberately.
lemcli apps refresh "$APP_UUID" --format=json
6. Add the users who may run it
An account admin creates the teammate identity. New account users currently receive app-view access by default; setting it explicitly keeps the intended authority visible.
read -rsp "Temporary member password: " MEMBER_PASSWORD
printf '\n'
lemcli account users create \
--username release-operator \
--email operator@example.com \
--password "$MEMBER_PASSWORD" \
--format=json
unset MEMBER_PASSWORD
export MEMBER_ID="<returned-user-id>"
lemcli account users permission toggle "$MEMBER_ID" \
--permission can_view_apps --value true
The app owner or app admin then grants only app execution. Adding an ACL entry grants individual access by default:
lemcli apps acls add "$APP_UUID" \
--user-id "$MEMBER_ID" --format=json
lemcli apps acls list "$APP_UUID" --format=json
If the recipe exposes shared scope, grant that capability explicitly. Grant admin only to someone who should manage the app and its ACLs.
lemcli apps acls toggle "$APP_UUID" \
--user-id "$MEMBER_ID" --permission shared --format=json
7. Member: discover and run
The member can now open the app in the browser or use the same server-owned action path through lemcli:
lemcli apps list --format=json
lemcli jobs surface app \
--uuid "$APP_UUID" \
--scope individual \
--format=json
export ACTION_ID="<returned-action-id>"
lemcli jobs run app \
--uuid "$APP_UUID" \
--action "$ACTION_ID" \
--follow \
--render html
Web and CLI callers receive the same task identity, system and recipe steps, live events, terminal reason, logs, and artifacts because both enter the same server-owned job path.
Cookbook collaborator or app runner?
Use cookbook ACLs only when another author needs to read or edit the source contract:
lemcli cookbooks acls add "$COOKBOOK_UUID" \
--user-id "$COLLABORATOR_ID"
lemcli cookbooks acls toggle "$COOKBOOK_UUID" \
--user-id "$COLLABORATOR_ID" --permission edit
Do not grant cookbook access to ordinary app members. An app ACL delegates the computation; a cookbook edit ACL delegates authorship and publication.
What the central authority proves
- the publisher was authenticated and authorized to edit that cookbook;
- the pushed image entered an account- and cookbook-scoped registry namespace;
- the recipe points to an immutable digest with recorded ownership and promotion metadata;
- the installed app binds a reviewed cookbook snapshot rather than a caller-selected image;
- the user passed both account visibility and app ACL checks before the job was admitted;
- browser and CLI clients observe the same durable execution record.