This is the AI-friendly usage guide for the foundation CLI. The implementation contract lives in
sdk/crates/cli/SPEC.md; use this guide when deciding which command to run for an SDK app workflow.
Use the canonical English command names in generated commands and docs.
- Run
foundation doctorbefore diagnosing missing SDK tools or environment setup. - Run app project commands from a directory inside the app project unless the command says otherwise.
- Do not run hardware-affecting commands such as
foundation sideloadorfoundation logsunless the user asked to use a connected Passport Prime or hardware. - Do not generate certificates, rotate signing identities, or edit
~/.foundation/signingunless the user explicitly asked for signing setup. - Do not run
foundation updateunless the user asked to install a newer SDK; it replaces the toolchain the current work is being built against. - Prefer
foundation previewfor UI-only Slint checks before using simulator or hardware. - Use
foundation simfor hosted app behavior checks when the app needs runtime callbacks or generated manifests. - Use
foundation buildfor signed hardware artifacts. It may prompt for signing identity and can modifytarget/keyos.
| Command | Use When | Run From | Preconditions | Side Effects and Outputs | Common Failures |
|---|---|---|---|---|---|
foundation new <name> [--template TEMPLATE] [per-field flags] [--no-git] |
Scaffold a new SDK app. name is required; every prompted field has a flag, so with no TTY it runs unattended from defaults. The app inherits the built-in Base Theme, with any app-specific theme overrides supplied by the template. |
Parent directory where the new project should be created. | SDK root must be discoverable; templates must exist in the SDK. | Creates a project directory, app config, template files, resources, a project-local .foundation-sdk/current SDK mapping, the SDK's agent skills, and a Git repo unless --no-git is set. |
Missing name, destination already exists, no templates found, invalid field values. |
foundation develop |
Enter the SDK Nix development shell. | SDK checkout, unpacked SDK bundle, or any place where FOUNDATION_SDK_ROOT is set. |
nix installed; SDK root discoverable. |
Starts a Nix shell and writes isolated shell config under ~/.foundation. |
Nix missing, SDK root not found, unsupported shell. |
foundation exit |
Clean Nix cache state after SDK development. | Anywhere. | nix-collect-garbage is useful but cleanup is best-effort. |
Runs Nix garbage collection and removes ~/.cache/nix; leaves SDK bundles and signing identities untouched. |
Cleanup warnings if files are missing or commands fail. |
foundation update [--no-verify] |
Install the latest published SDK release. | Anywhere. | curl or wget, sh, gpg and gpgv, network access to the release channel, and a running SDK that shipped its release key. |
Downloads the release channel's install.sh, verifies its signature against the release key of the running SDK, and only then runs it: unpacks the new release into the install root, repoints current and the launcher, and rewrites the shell rc PATH block unless FOUNDATION_SDK_UPDATE_RC=0. --no-verify skips every signature check, on the installer and on the archives (unsafe). |
Installer signature not from the SDK's release key, running SDK carrying no release key, gpg or gpgv missing, download failure, unsupported host platform, install root not writable or not owned by the user. |
foundation doctor |
Inspect local SDK/app build readiness. | Anywhere, but SDK discovery is clearer from an SDK or app directory. | None beyond the CLI itself. | Prints tool, SDK, and target checks with suggested fixes. | Reports missing Nix, Cargo, Rust target, strip tool, cosign2, viewer, or Git. |
foundation docs [SDK_VERSION] [--url] |
Browse the complete static API docs bundle from the current or a specific installed SDK. SDK_VERSION selects the SDK installation; the selector in the page chooses a KeyOS API version. |
Anywhere. | The selected SDK must include the versioned docs/api bundle. |
Opens the bundle's file:// URL in the default browser. Pass --url to print the URL instead. It does not start a server or retain any state. |
SDK/version or complete docs bundle missing, or browser launcher unavailable. |
foundation skills |
Install the agent skills the SDK ships into the current directory, where agent tools resolve them. | Inside an app project, or in a repository being ported to the SDK. | SDK root discoverable. | Copies every SDK skill into .claude/skills/<name>/ and .agents/skills/<name>/, replacing an earlier copy and leaving the app's own skills untouched. |
SDK root not found, SDK ships no skills, write failure. |
foundation build [--release] |
Build, stage, and sign a hardware-targeted app bundle. | Inside an app project containing app-config.toml. |
Cargo, arm-none-eabi-strip, cosign2, signing config, SDK root, and suitable Nix shell. |
Writes target/keyos/<app-name>/app.elf, manifest.json, icon.bin (plus icon-dark.bin when the app ships a dark icon), and resources; converts app icons/images to .raw; copies app fonts. |
Not in SDK shell, missing signing identity/config, missing icon, unsupported or invalid asset, missing permission template, Slint codegen/preflight failure. |
foundation clean |
Remove generated app build and theme artifacts. | Inside an app project containing app-config.toml. |
None beyond the CLI itself. | Removes target/ (cargo output, generated target/foundation/** UI/resources and theme json/rust/slint, and target/keyos/** bundles), the generated manifest.toml, the ui/ui mapping, and .foundation-sdk/; leaves authored source and ~/.foundation/themes untouched. |
Not in an app project, file removal failure. |
foundation theme [filename] |
Open the visual theme editor for an explicit theme JSON, or for the current app when no filename is given. | Anywhere with a filename; otherwise inside an app project containing app-config.toml. |
SDK root discoverable; bundled foundation-theme-editor or source checkout fallback available. |
Opens the supplied file directly without rewriting it. Without a filename, ensures theme in app-config.toml points at an editable JSON file, creates resources/theme.json when needed, and passes that file to the editor so Save updates it directly. |
Editor missing, invalid theme JSON, or (without a filename) not in an app project/configured base theme not found. |
foundation preview [file] [options] |
Open a Slint UI in foundation-slint-viewer without a hardware build. |
App project or path near the target .slint file. |
Viewer available; SDK root preferred for @ui imports. |
May run app build-script preflight to materialize ui/gen; launches viewer with SDK UI include paths. |
Missing viewer, unresolved @ui imports, generated Slint files missing, Cargo preflight failure. |
foundation sim |
Build the app for hosted execution and launch the simulator. | Inside an app project containing app-config.toml. |
SDK root discoverable; Cargo available; bundled or PATH simulator available. | Builds debug app, stages it under the SDK simulator app directory, writes manifest, copies app resources into that bundle, and launches simulator with target/foundation/sim-resources. |
SDK root not found, simulator not found, hosted build failure, asset staging failure, launch failure after staging. |
foundation pack [--release] [--out PATH] |
Build/sign the app and wrap the bundle into one .app archive a user can install from Settings, with no USB debug or Developer Mode. |
Inside an app project containing app-config.toml. |
Everything needed by build. |
Writes target/keyos/<app-name>.app: a gzip-compressed tar of manifest.json (first), then every file it hashes, sorted by name (app.elf, icon-dark.bin and icon.bin when present, resources). |
Build/signing failure, --out pointing inside the bundle or at a symlink, bundle over the 64 MiB an install accepts, archive write failure. |
foundation sideload [--release] [--no-run] |
Build/sign/upload an app to connected Passport Prime hardware and optionally launch it. | Inside an app project containing app-config.toml. |
Everything needed by build; Passport unlocked and connected by USB; Developer Mode enabled. |
Uploads signed app.elf, manifest.json, icon.bin (plus icon-dark.bin when present), and generated resources over usb-debug to the sideloaded bundle directory named by the app ID; may send launch command over USB. |
USB debug unavailable, Developer Mode disabled, upload failure, MCP launch failure, build/signing failure. |
foundation logs [--timeout SECONDS] |
Open the Passport USB log viewer for connected hardware. | Anywhere. | foundation-keyos-log-viewer bundled or on PATH; Passport connected over USB. |
Launches the log viewer and attempts USB discovery/reconnect. | Viewer missing, no device found, USB permission or reconnect timeout issues. |
foundation cert gen [name] [--publisher-name NAME] [--contact-email EMAIL] [--support-url URL] |
Create a publisher signing identity for app builds. | App project when using app publisher defaults, otherwise anywhere. | OpenSSL available; explicit user intent to create signing material. | Writes private key, public key, certificate, and cosign2.toml under ~/.foundation/signing/<name>. |
Missing required publisher metadata, invalid identity name, OpenSSL failure, accidental duplicate identity. |
foundation cert print [name] |
Inspect a stored publisher certificate. | App project for default identity lookup, otherwise anywhere. | OpenSSL available; certificate exists. | Prints decoded certificate details. | Identity not found, certificate missing, OpenSSL failure. |
foundation cert fingerprint <cert> |
Print the stable identity users should compare before allowing a publisher. | Anywhere. | OpenSSL available; PEM or DER X.509 certificate exists and contains a secp256k1 public key. | Prints the full and short canonical publisher fingerprint; does not modify the certificate. | Certificate missing or malformed, unsupported public key or curve, OpenSSL failure. |
foundation cert install [name] |
Review a stored publisher certificate and allow it on connected hardware. | App project for default identity lookup, otherwise anywhere. | OpenSSL available; certificate exists; Passport unlocked and connected by USB with Developer Mode enabled; interactive user confirmation. | Shows the unverified-identity warning and full/short fingerprint before the prompt, then installs the certificate only after the user chooses Allow; declining makes no changes. | Non-interactive session, identity not found, fingerprint extraction failure, USB debug unavailable, Developer Mode disabled. |
foundation completions <bash|zsh|fish|powershell> [--stdout] |
Install or generate shell completions. | Anywhere. | Shell name must be supported. | Writes completions to the user shell completion directory by default, or prints them with --stdout. |
Unsupported shell, install path creation/write failure. |
CLI plugins are temporarily unavailable in supported SDK builds while the installation and publisher trust model is redesigned. The implementation is retained only for maintainer testing behind a default-off Cargo feature; do not recommend plugin commands to SDK users.
Cargo.toml: the app package's canonical version. The build copies this version into the signed manifest and executable signing header.app-config.toml: KeyOS app metadata, app ID, icon path, theme path, minimum KeyOS version, permissions, optional QR match rules, and optional signing identity. Older projects may still contain a duplicateversion; it is accepted only when it matchesCargo.tomland should be removed. Theiconfile is converted into the bundledicon.binplaced next toapp.elf. A<stem>-dark.(svg|png)sibling of the icon (e.g.resources/icon-dark.svg) is converted intoicon-dark.binbeside it and used in dark theme; without one, the light icon serves both themes. The optionalthemeentry names an app-local theme JSON path, typicallyresources/theme.json.permission_templates.toml: named permission bundles expanded byapp-config.toml..foundation-sdk/current: generated SDK dependency mapping used by templateCargo.tomlpath dependencies. It is ignored and refreshed byfoundation new,build,sim, andpreview.ui/app.slint: default UI entrypoint forfoundation preview.ui/gen/*: generated Slint router, translation, and export files. Do not edit by hand.i18n/*.json: app translations.resources/: app icon, app theme JSON, and app-owned resources. Put app images inresources/imagesand app fonts inresources/fonts; these app-owned files are searched before SDK shared resources.images/andfonts/: legacy app-owned asset directories. Hardware and simulator builds still stage them for compatibility, but new apps should preferresources/imagesandresources/fonts.target/keyos/<app-name>/: hardware build output fromfoundation build, includingapp.elf,manifest.json,icon.bin(andicon-dark.binwhen the app ships a dark icon), and app-localresources.target/keyos/<app-name>.app: the same bundle packed byfoundation packinto one file, for installing on a device from a USB drive or the airlock.
Apps can register QR match rules for the Launcher QR scanner tile in app-config.toml.
When the Launcher scan result matches one app, KeyOS opens that app directly. When several apps match,
KeyOS asks the user which app should handle the QR data.
Apps launched from a QR match also need permission to read the handoff payload. Declaring any rule
grants the app the os/gui-server GetPendingNavRequest permission, so the app config does not have
to request it.
Use [[qr-match-rules]] entries. Like every other key in app-config.toml, the rule fields are
kebab-case; the CLI converts them into the camelCase manifest form when it generates manifest.json.
[[qr-match-rules]]
id = "otpauth"
priority = 5
id-localizations = { en = "OTP Auth" }
sub-rules = { qr = { QR = { regex-pattern = "^otpauth://" } } }
[[qr-match-rules]]
id = "crypto-psbt"
id-localizations = { en = "PSBT" }
sub-rules = { ur = { UR = { ur-type = "psbt" } } }id is the stable rule identifier reported to your app with the scan result. id-localizations is the
user-facing label shown when the Launcher needs a choice. priority is optional and defaults to 3;
valid values are 1 through 5, with higher values sorted first.
Each entry in sub-rules is keyed by a name you choose and tagged QR or UR. QR sub-rules match raw
QR payloads: use min-len, max-len, and regex-pattern to narrow the match. UR sub-rules match UR 2.0
payloads by ur-type. Unknown fields are rejected, so a misspelling fails the build instead of shipping
a rule that never matches.
Use app-owned asset folders under resources/ when adding new user assets:
resources/images/:.svg,.png,.jpg,.jpeg,.webp, and.bmpresources/fonts/:.ttf,.otf, and.ttc
Reference app images from Slint with Images.image("<relative-path-without-extension>"). For example,
resources/images/logo.svg becomes Images.image("logo").
Fonts in resources/fonts/ are registered before the Slint app starts. Use the font family's real name in Slint
font-family properties; the file name only controls how the file is copied.
SDK shared fonts, icons, and images are searched automatically after local app resources. App projects should not create
symlinks from resources/fonts, resources/icons, or resources/images back to the SDK.
In KeyOS v1, the user allows a publisher; Foundation does not verify that publisher's identity. Allowing a publisher means that apps signed by its key may run on that user's Passport. The certificate's publisher name, organization, email address, and support URL are self-asserted claims, not proof of who controls the key.
Before foundation cert install asks the user to allow a publisher, it displays:
Foundation has NOT verified this publisher's identity
This warning means that neither Foundation nor the device has confirmed the claimed identity. Compare the displayed fingerprint with a value the publisher provides through a separate, official channel before choosing Allow. The fingerprint is the publisher's key identity; a matching claimed name is not sufficient.
The canonical publisher fingerprint is the SHA-256 digest of the compressed 33-byte secp256k1 public key, rendered
as 64 lowercase hexadecimal characters. The short display form is the first four bytes and last four bytes of that
digest, rendered as xxxxxxxx…xxxxxxxx. The short form is for recognition only; compare the full fingerprint when
making the allow decision.
foundation cert gen prints both forms after creating the key and certificate.
foundation cert fingerprint <cert> prints them again later. Publishers should place the full fingerprint on their
official website and/or official GitHub repository so users can verify it out-of-band.
After an allow decision, the CLI passes the reviewed full fingerprint to passport-drive. Passport-drive re-parses the bytes it will send, and firmware previews the same certificate again; both layers require the fingerprint to match before import.
Publishers are also encouraged to publish this DNS TXT record:
_keyos-publisher.<domain> TXT "v=1; k=secp256k1; fp=<hex>"
Replace <domain> with the publisher's official domain and <hex> with the full 64-character lowercase
fingerprint. This is a publication convention only in v1: KeyOS and Foundation do not retrieve or verify the record.
It is intended as a stable home for a future attestation service.
The app hash is the SHA-256 digest of app.elf without its 2048-byte cosign2 signature header, rendered as 64
lowercase hexadecimal characters. The header is excluded because it stamps the wall-clock time of the signing
run, and the signature covers those bytes, so two reproducible builds of the same source produce the same app
hash but different signed files.
foundation build prints the app hash after signing. The bundle's manifest.json records it under
fileHashes, where the manifest signature covers it, and KeyOS re-checks it against the ELF's own header on every
launch. On the device the same digest appears under Settings > Apps > app > App Hash, grouped in fours for
readability; ignore the spaces when comparing.
A user can recompute it from any signed app.elf:
tail -c +2049 app.elf | sha256sumWhat publishers distribute is the packed .app bundle, which is a gzipped tar holding app.elf alongside
manifest.json, the icons and the resources. Hashing that archive is not the app hash; extract the ELF first:
tar -xOf myapp.app app.elf | tail -c +2049 | sha256sumPublishers with a reproducible build should publish the app hash of each release alongside their fingerprint, so users can check the app on their Passport against the published value rather than only against a build they made themselves.
The SDK ships Codex and Claude skill files under .agents/skills/ and .claude/skills/ in the SDK bundle.
Agent tools resolve skills from the directory they are started in, so those files are out of reach from an app
checkout: run foundation skills in the app to copy them in, and again after an SDK upgrade to refresh them.
foundation new installs them into every new app. They are prompt workflows, not foundation CLI subcommands:
/foundation-clicovers choosing and running the commands in this guide./foundation-localize <source-locale> <target-locale>updatesi18n/<target-locale>.jsonfromi18n/<source-locale>.jsonwhile preserving keys, placeholders, product names, and valid JSON formatting./foundation-new-page <name>adds a new routed Slint page to a router-enabled app by creatingui/pages/<name>/page.slintandui/pages/<name>/props.slint, then wiring navigation only when the existing app structure makes the insertion point obvious./foundation-sdk-migrate-newest [path]ports an app to the newest SDK by followingMIGRATIONS.md.
foundation sideload uploads files to a Passport Prime over usb-debug and can launch an app on the device.
Use --no-run when the user only wants the bundle uploaded; it still verifies passport-drive MCP and Developer Mode
before writing.
On Linux, every USB device interaction (sideload, logs, cert install, passport-drive MCP) requires the udev
rules shipped at <sdk-root>/share/99-passport.rules. A permission error opening the device means they are not
installed: copy the file to /etc/udev/rules.d/, then sudo udevadm control --reload && sudo udevadm trigger.
foundation cert gen creates long-lived signing material in the user's home directory. Treat this as a user
identity operation, not a routine build step. If a build fails because no signing identity exists, explain the
required setup instead of generating one silently.
foundation cert install is an identity-allow operation and requires an interactive confirmation. Do not bypass or
automate the warning. The user must compare the full fingerprint against the publisher's official website or GitHub
before allowing it.
foundation build signs app artifacts and may prompt for an identity. In non-interactive agent runs, prefer setting
signing-identity or cosign2-config in app-config.toml only when the user has identified the intended publisher.
- Environment issue: run
foundation doctor. - Slint/UI issue: run
foundation preview ui/app.slint --auto-reloador a targeted preview command. - Hosted runtime issue: run
foundation sim. - Hardware install issue: confirm user intent, then run
foundation sideload --no-runbefore launching. - Signing issue: inspect
app-config.toml, then explain the neededfoundation cert genor config change.