Agent Guide teaches coding agents to operate the current AppThrust Project with
apth and apthctl. A Go server serves a React SPA, authenticated Markdown,
llms.txt, downloadable skills, and the /api/agent-guide/v1 API. Curated content
is embedded in the image and rendered with the installation's environment values.
There is no database, service credential, or platform write performed by the server.
The browser's Agent setup panel creates an Access Key with the user's own bearer.
cmd/server/: entrypoint, routes and fixture-based tests.cmd/server/internal/content/: frontmatter validation, ordering and indexes.cmd/server/internal/render/: strict Go text/template rendering.cmd/server/internal/api/: caller-specific Platform context.content/: release-versioned documents and agent skills;embed.goembeds them.web/: Vite/React SPA, development mock and browser-side Platform API access.catalog/: application definition, localized listing and screenshots.hack/: catalog publication and source provenance checks.docs/design.md: domain model and HTTP/content contracts.
The public Go SDK is github.com/appthrust/appserver-go v0.3.0, matching the Memo
template. No platform checkout, module replacement or private Go credential is
needed to build. The launcher and i18n packages come from GitHub Packages.
Content source references pin platform commits; the authoring snapshot is
1572ae7afa15df418cad36501f237b4595349428.
Use Go 1.26 or newer and Bun 1.4.1:
GOFLAGS=-mod=mod go test ./cmd/...
go vet ./cmd/...
cd web
# Supply a GitHub Packages credential with read:packages through the environment.
bun install --frozen-lockfile
bun run typecheck
bun test src
bun run build:imageFor standalone UI development, run bun run mock and bun run dev in separate
terminals in web/. To serve the built SPA and API together:
GOFLAGS=-mod=mod go run ./cmd/server --static-dir web/distThe server reads the following environment variables (the platform connection and catalog supply these in an installed app):
| Variable | Meaning |
|---|---|
PLATFORM_API_URL |
Required Platform API origin; HTTPS except loopback HTTP. |
PLATFORM_PROJECT_ID |
Required Project DNS-label identifier. |
PLATFORM_OIDC_ISSUER |
User-token issuer; required unless listening on loopback. |
PLATFORM_OIDC_ISSUER_CA_PEM |
Optional certificate-only bundle augmenting system trust. |
PLATFORM_ACCESS_ISSUER |
Access Key assertion issuer, distinct from the OIDC issuer. |
AGENT_GUIDE_OIDC_AUDIENCES |
Comma-separated trusted user-token audiences; required with the OIDC issuer. |
AGENT_GUIDE_ACCESS_AUDIENCE |
Access Key assertion audience; unset disables assertions. |
AGENT_GUIDE_PUBLIC_URL |
Required guide origin for rendered links. |
AGENT_GUIDE_STATIC_DIR |
SPA directory, default /srv/agent-guide; overridden by --static-dir. |
AGENT_GUIDE_RELEASE_VERSION |
Version used in content and /releasez. |
AGENT_GUIDE_ARTIFACT_DIGEST |
Image digest used with the version by /releasez. |
AGENT_GUIDE_SOURCE_REVISION |
Optional source revision for /releasez. |
AGENT_GUIDE_RELEASE_FILE |
Optional release JSON file, used when version/digest are incomplete. |
--listen defaults to :8080. For issuer-free local development, explicitly use
--listen 127.0.0.1:8080; the SDK warns that bearer claims are then unverified.
Do not expose that mode to a network. Never place credentials in committed env
files. NODE_AUTH_TOKEN is a web dependency/build credential, not a server setting.
Content parses and renders once at boot. Invalid frontmatter, unsupported template
fields, or template errors are logged; the process and /healthz stay alive,
/readyz returns 503, and content routes return content_unavailable. Tests use
only cmd/server/internal/content/testdata/, never the concurrently authored tree.
The SPA, /config, /releasez and health probes are public behind the platform
edge. Every content/API route requires a bearer. Access Key assertions must match
this Project and include scope read; a raw secret is not accepted.
The context endpoint never grants operator authority. It returns the verified
email claim as me.login, and the Platform client reads the caller's Project role
from /api/v1/projects/{id}/access. It forwards the original bearer unchanged, using a
fresh client per request so no context or role cache crosses callers. Installation
name is the platform installation id, not its display title. Keys receive
403 key_subject; upstream failures receive 503 platform_unavailable.
Open Agent setup in the signed-in SPA to create a read-only Access Key and see
its secret once. The app never stores the secret. Alternatively, use apth with
your selected profile (set the non-secret origins and Project identifier first):
apth login --api-url "$PLATFORM_API_URL" --project "$PLATFORM_PROJECT_ID"
apth access-keys create --project "$PLATFORM_PROJECT_ID" --space agent-guide --scope read --ttl 720h --label coding-agent --envKeep the one-time key only in the agent's secret environment. Exchange it without printing the assertion, then fetch the guide:
export AGENT_GUIDE_ACCESS_URL="$PLATFORM_API_URL"
# AGENT_GUIDE_API_KEY contains the one-time secret returned by key creation.
assertion="$(printf '%s' "$AGENT_GUIDE_API_KEY" | apth access-keys exchange --access-url "$AGENT_GUIDE_ACCESS_URL")"
curl -fsSL -H "Authorization: Bearer $assertion" "$AGENT_GUIDE_PUBLIC_URL/llms.txt"
unset assertion AGENT_GUIDE_API_KEYAccessURL is the Platform API origin: exchange POSTs /api/v1/access/token
there. PublicURL is the guide origin used for /llms.txt, /md/… and skills.
These origins are intentionally distinct. Disable shell tracing while handling
secrets; never paste real keys or assertions into docs, logs or screenshots.
/llms-full.txt concatenates all documents; /skills/index.json lists each skill's
SKILL.md and reference files. The same bearer authorizes their downloads.
Documents are Markdown under content/, written in English, with this frontmatter:
---
title: Log in and choose a Project
summary: Device login and Project selection.
audience: developer
section: apth
order: 20
tags: [auth, profiles]
sources:
- path: apth-cli/README.md
revision: 1572ae7afa15df418cad36501f237b4595349428
---Every document needs a title, summary, audience (developer, operator, both),
section, explicit order, tags array and non-empty sources. Slugs are relative
paths without .md. Sections sort as start-here, apth, apthctl, concepts,
lessons, then numeric order, title and slug. Document API details omit order;
the index includes it. Sources name paths in the pinned platform Git revision.
Only these Go template fields are allowed: {{.PlatformAPIURL}},
{{.ProjectID}}, {{.PublicURL}}, {{.BaseDomain}}, {{.AccessURL}}, and
{{.ReleaseVersion}}. No functions, conditionals or custom fields. BaseDomain
is the public hostname with the exact agent-guide.<ProjectID>. prefix removed;
custom hostnames remain unchanged. Rendered documents omit frontmatter. Skills
live in content/skills/<name>/SKILL.md, require matching name and description
frontmatter, and may carry provenance plus references/*. Skill downloads retain
frontmatter and use the same renderer.
Every document and every skill's SKILL.md has a Japanese variant beside it, <name>.ja.md
(content/start-here.ja.md, content/skills/<name>/SKILL.ja.md). A variant
translates title, summary and description and the prose; it keeps
audience, section, order, tags and sources identical (content validation
fails otherwise), keeps skill name, and never changes placeholders, commands, code
blocks, flags, URLs or condition names. The slug comes from the English path.
?lang=ja selects the variant on every content route (English when a variant
is missing); the agent text routes stay English without it, and the SPA API also
honours Accept-Language. In a variant, links to other guide documents and
skill files that have a variant gain ?lang=ja at render time. Responses carry
lang (served) and availableLangs.
Use apth commands where available, explicit --yes on destructive operations,
and --wait --timeout on waits. Never hardcode estate hosts, Project IDs or
credentials. Operator content must state required authority, not provide it.
Before submitting content changes, run against a local platform checkout that contains the pinned revisions:
bun hack/check-sources.ts --platform /workspace/platformThe checker runs git cat-file -e <revision>:<path> for every source, reports every
missing reference and exits non-zero. It is intentionally not a CI job because
the platform repository is not readable in this repository's CI. CI still validates
Go routes/rendering, the web client and the catalog publisher without that checkout.
catalog/entry.yaml starts with versions: [], scope read, bearerPassThrough,
follow by default and pinned as an option. It uses the bundled terminal icon.
There is one web component and no PostgreSQL/CNPG dependency.
Build the image with a required BuildKit package secret:
docker build --secret id=NODE_AUTH_TOKEN,env=NODE_AUTH_TOKEN -t agent-guide-local .The build compiles the SPA natively and the Go binary for the target architecture.
The non-root distroless runtime uses /agent-guide-server, /srv/agent-guide,
UID 65532 and port 8080. No JavaScript runtime or package credential enters it.
A SemVer tag such as v0.1.0 builds amd64/arm64 images, signs the image index with
Cosign and publishes an immutable digest through Cloud's catalog. Publication
commands retain Memo's interfaces:
bun hack/publish-entry.ts submit --version 0.1.0 --digest sha256:<image-index-digest>
bun hack/publish-entry.ts await --version 0.1.0 --digest sha256:<image-index-digest>
bun hack/publish-entry.ts validate-listing
bun hack/publish-entry.ts listingThe publisher preserves prior versions and refuses to replace immutable images.
await verifies the exact version/digest in the signed catalog using ORAS and
Cosign; dispatch alone is not publication proof. Listing-only changes do not imply
a release. The workflows share agent-guide-catalog-publish and are guarded to
appthrust/agent-guide.
Required repository secret: CLOUD_CATALOG_ADMIN_TOKEN; optional variable:
CLOUD_API_URL (default https://api.appthrust.com). Organization App secrets
APPTHRUST_GITHUB_ACTIONS_APP_ID and APPTHRUST_GITHUB_ACTIONS_APP_PRIVATE_KEY
allow a short-lived token to dispatch Cloud publication. Grant this repository's
GITHUB_TOKEN read access to the shared GitHub Packages and make the GHCR image
public after first publication. Publisher regression tests run with bun test hack.