CLI reference
The kya-os commands and flags for setting up Vouched KYA, how --project resolves, and the environment variables and files the CLI reads and writes.
From kya-os 2.5.1, JavaScript builds use a caret dependency on @kya-os/checkpoint-beacon, and their build, dev, and typecheck commands synchronize companion assets from the installed version. The Java dependency floor is 0.2.2. The beacon's reload persistence needs @kya-os/checkpoint-beacon 1.4.4 or later.
kya-os --help and kya-os <command> --help list everything the installed version supports.
setup
A live sign-in on the same Vouched KYA origin is reused, so a second run doesn't open the browser. If the project you name isn't part of that sign-in, setup signs in again with it preselected so you can grant it. A name no project has yet is offered on the consent page instead, to create and grant in one step. With nothing named, the offer is this repository's name, and a granted project by that name is linked without asking.
detect install
Without a sign-in, detect install uses the project you name as given, since the pixel and beacon need only the project ID; it says so. What it writes:
On .NET, the page <head> is in a different file for each project type:
- Blazor Web App:
Components/App.razor - Blazor WebAssembly:
wwwroot/index.html - Blazor Server upgraded from .NET 7 or earlier:
Pages/_Host.cshtml(Pages/_Layout.cshtmlif it started on .NET 6) - MVC:
Views/Shared/_Layout.cshtml - Razor Pages:
Pages/Shared/_Layout.cshtml - Web Forms (.NET Framework):
Site.Master
With --surface sdk and no runtime key where the app reads it, a signed-in detect install offers to mint the app's key. It asks first; --yes skips the prompt, and a dry run only says it would. The key is named kya-os CLI · sdk · <host> - <repository>, is returned once, and has read permission. The plan's API key limit applies, as it does to dashboard keys.
- Next.js and Express: the CLI adds the env file to
.gitignore, then writes the key to.env.local(Next.js) or.env(Express). - ASP.NET Core: the CLI runs
dotnet user-secrets initfirst, then stores the key and the project ID asCheckpoint:ApiKeyandCheckpoint:ProjectIdin the project's user secrets, outside the repository. The key reachesdotneton stdin, never its command line. In production, setCheckpoint__ApiKeyandCheckpoint__ProjectIdin the host's environment. - Java: the CLI prints where the key goes.
Signed out, detect install says to sign in with kya-os login and re-run it, which mints the key.
The CLI adds or updates its generated Checkpoint Detect wiring. It leaves edited or unrecognized files alone and prints the changes to compose manually. An existing middleware.ts or proxy.ts is never overwritten; the lines to compose into it are printed instead. A failed package install stops the run before anything that imports the package is written.
Browser posture
--surface beacon installs the full @kya-os/checkpoint-beacon package for JavaScript builds. Every project has a posture key for the signed token your server verifies. Set up browser posture walks through browser and server configuration.
For package installs, the CLI resolves the full beacon package and adds a caret range, then uses the version actually installed to supply its worker and lazy signals files. The generated root checkpoint-beacon-assets.cjs script copies both companion files and writes checkpoint-beacon-assets.json beside the generated client module. Whichever of the application's build, dev, and typecheck scripts exist now call it with --run, which synchronizes the assets and then runs the original command. The CLI saves each original command as checkpoint:build, checkpoint:dev, or checkpoint:typecheck. Beacon initialization imports those URLs. A repeated install does not wrap the scripts twice, and it does not depend on a postinstall hook. Running the helper again reads the installed package, so a lockfile update cannot leave the old worker paired with new beacon code.
For script tags, the CLI resolves the published beacon, verifies the npm tarball against its registry integrity, computes the script's SHA-384 hash, and checks the matching jsDelivr bytes. The tag is pinned to that version with integrity and crossorigin="anonymous". These installs retain the pinned script path.
Use the shell's beacon instance in the shared API transport. The following browser-only example reads posture at the moment the request starts:
import { CheckpointBeacon } from '@kya-os/checkpoint-beacon';
const beacon = new CheckpointBeacon({ projectId: '<project UUID>' });
export function generate(body: string) {
return fetch('https://api.example.com/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...beacon.getPostureHeaders(),
},
body,
});
}getPostureHeaders() is synchronous and adds no Vouched KYA network call to the request. It returns the active session header and, when available, an unexpired token for that project and session. It returns no posture headers after the session's idle deadline. The getter never waits for collection, renewal, or a token. A new visit has no signed token until the first ingest response, so a request sent with the getter alone carries only the session header; an HTTPS reload can restore an unexpired token for the same project and session. To give a page's first call a chance at a token, await prepareForAction({ action }) before it, or send it through the hosted script's checkpointFetch, which waits once, up to 3 seconds. Missing posture remains unknown and grants no permission.
Use this read only for your own API. Read it again on every request and replace any earlier posture headers; never save them as client-wide defaults. One application shell owns initialization, and micro-frontends use its current instance or exported header getter. The CLI does not rewrite existing application API calls.
A caret range permits compatible package updates when dependencies are resolved. A frozen lockfile and a deployed bundle stay on their existing version until you update, rebuild, and deploy. Keep the generated asset synchronization step in both build and dev commands.
A real install refuses a beacon older than 1.4.3 because it lacks posture forwarding. A dry run reports the gap without changing the application. The newer persistence behavior needs @kya-os/checkpoint-beacon 1.4.4 or later, even when an older release already supports getPostureHeaders().
The CLI leaves edited initialization and proxy files for manual composition. Check that the generated synchronization command still runs before your build and that both asset URLs reach files from the installed version.
Configuring posture
Every project has a posture key, so there is nothing to turn on. On every surface the CLI calls Vouched KYA's management posture API: it reads the project's posture (the first read creates the key, and an existing key is never rotated) and, with --origins, replaces the additional origins. All of it needs a sign-in, and setup signs you in as part of the command.
The posture calls run even when the install plan isn't applied, because there's no terminal and no --yes, or you answered No. --origins still changes Vouched KYA.
The CLI checks the key and prints the configuration:
Java dependency instructions select the latest stable Maven release at or above 0.2.2, with 0.2.2 as the fallback. Dependency and verifier snippets are printed for you to apply; the CLI does not edit Maven or Gradle files or install the Java configuration. Confirm that version is published before using it. A pinned public key verifies posture locally. BrowserPostureOptions.remoteKeys(...) fetches the keys in the background, so no request waits for a fetch. checkpoint.warmup(), which the servlet and JDK HTTP server filters call at startup, runs the first fetch before traffic. A request finds no key, and fails posture verification, before the first fetch succeeds and after a refresh fails. Pin the public key if posture verification must not depend on the keys endpoint being reachable. After rotating the project key, update the server's key ID and coordinates before activating the new signer.
Origins the Java and .NET verifiers would reject are left out of the config blocks, with the note These origins are left out of the verifier config because Java and .NET would reject them: .... They are empty labels, odd characters, labels over 63 characters or edged by -, and single-label extras. A single-label primary domain such as https://localhost:3000 is accepted. Additional origins need two labels, and --origins refuses single-label hosts.
Proxy targets
--proxy <path> takes letters, digits, _, -, and /, up to 64 characters including a leading /, with no trailing /, no //, and no empty segment. The beacon's endpoint becomes <path>/api/v1/beacon, prefixed by a Next.js basePath when the CLI can read one.
When you're signed in, the CLI mints a project API key with the proxy purpose and the forward permission only, and stores it as CHECKPOINT_PROXY_KEY. Minting needs a project owner or admin, and the key counts against the plan's API key limit. The key is shown once and never printed. Signed out, the CLI asks you to sign in and re-run, or to set the variable yourself. Forward edge evidence covers what the key can do.
The nginx block and the Akamai rule clear Cookie, Authorization, and the JA4, challenge, and challenge-id headers by name. Delete any other KYA-Forwarded-* header yourself. For nginx, Cloudflare, and Akamai, nothing is applied.
The generated Next.js proxy behaves like this:
The Content-Security-Policy directives the CLI prints depend on whether the proxy landed and how the script is delivered:
- A nonce-based policy still needs the response nonce and
'strict-dynamic';'self'alone does not authorize a script under that policy. - It always lists
base-uri 'none'(add it with a nonce-based policy) andtrusted-types checkpoint-beacon(add it when the page enforces Trusted Types). Add them only if they apply. - A project with no static directory still loads the script from jsDelivr, so
script-srckeeps the CDN, and the CLI says so. - The CLI reports the collapsed policy only when every step landed. A skipped or failed step counts as not in place.
- With
--proxyand a static directory, an HTML page's script tag also points at a self-hostedbeacon.min.js, written to the same_checkpoint/<version>/directory.
posture in --json
Both setup and detect install add a posture object to their report. Outer keys with no value are left out. Inside state, primaryOrigin is null when the project has no domain.
The proxy key's value is never in the report. A minted key appears in keys by keyId, name, and preview, and the plan lists a writeSecret action for where it was stored.
detect doctor
kya-os detect doctor --url <page> fetches a page and checks its beacon configuration, files, and headers. It reads the HTML, then probes the files and headers the beacon needs. When the page has no beacon.min.js tag, it also reads up to 20 same-origin scripts to find a beacon inside a bundle.
It exits 1 when any check is fail, and 0 for warn and info.
The doctor does not run the application. In a browser, confirm that initial API requests proceed before a token exists, later requests carry current posture headers, the API allows the required CORS headers, and the server reports the verified outcome. Repeat after reload and a session change. A zero exit code does not establish these runtime checks or load capacity.
login, logout, whoami, projects
The sign-in is a management session for a person. It lists your projects and links repositories to them, and Vouched KYA's runtime API refuses it, so it can never stand in for an app's API key. Access tokens last 15 minutes and renew silently; the session ends after 30 days without use, and after 90 days regardless. On Linux the session needs a Secret Service keyring (GNOME Keyring or KWallet); see Troubleshooting for machines without one.
mcp create
kya-os mcp create <name> runs @kya-os/create-mcpi-app for Cloudflare Workers, the runtime that pulls per-tool protection from Vouched KYA. It's pre-filled with the linked project and your repository's package manager, and runs with no prompts. When you're signed in, it then mints the server's key (read and write, for proofs and tool discovery) into the new server's gitignored .dev.vars. The key never goes on the scaffolder's command line.
Without a terminal, pass --yes or --no-key: the key is decided before anything is scaffolded, so a server is never left half set up. The scaffolder needs Node.js 22.12 or newer.
Then run it locally with npm run dev (the CLI prints your package manager's form). Before the first npm run deploy, sign wrangler in once with npx wrangler login, then upload the server's secrets, the key among them, with npx wrangler secret bulk .dev.vars.
govern
kya-os govern install --agent claude adds a Claude Code PostToolUse hook to this repository. After every tool call, Claude Code runs kya-os hook post in the background, and the call appears in the project's Activity feed as an observed verdict. Nothing is blocked.
- Sent: the tool's name, the session, and the call's ID. The tool's input and output are never sent, since they can hold file contents and secrets.
- Never in the way: the hook runs asynchronously, gives up after three seconds, and exits 0 on any failure.
- Key: the project's read-only runtime key, read from the repository's gitignored
.env.localor.env. If the key is only in your shell'sCHECKPOINT_API_KEY,installsaves it to.env.local, because the hook runs inside Claude Code, which may not see your shell. When there's no key and you're signed in,installmints one. - Origin: the hook reports only to the Vouched KYA origin it was installed for (
hook post --origin …), so a later edit to the committed link can't redirect the key.
Your other hooks and settings are kept, and a second install changes nothing, unless the hook points at another Vouched KYA origin or another kya-os binary: then it's replaced, and the state is updated. kya-os govern uninstall --scope <scope> removes only the kya-os hook.
How --project resolves
Every command that needs a project takes the first of:
- the
--projectflag; - the
KYA_OS_PROJECTenvironment variable; - the nearest
.kya-os/project.json, from the current directory up to the repository root; - the only project the sign-in covers.
A value matches a project by ID, or by exact name, ignoring case. A name shared by several projects is refused with the list of matches, and with several projects and nothing selected, setup asks, offering a new project too (or, without a terminal, lists them and stops). Only a project ID preselects a project on the consent page; any other value is matched there by name, or offered as the name of a new project.
Environment variables
The installer's own variables are on Installation.
Files
{
"schema": "kya-os.project-link.v1",
"origin": "https://kya.vouched.id",
"organizationId": "5b1f3a9e-…",
"projectId": "9c2d7e41-…",
"name": "acme-web"
}A link that names another Vouched KYA origin (staging, a local server) is ignored, with a message, rather than resolved against the wrong one.
JSON output
With --json, a command prints one JSON document on stdout, and progress, prompts, and package-manager output go to stderr. setup reports the link and what Detect did:
{
"origin": "https://kya.vouched.id",
"signedInNow": true,
"organizationId": "5b1f3a9e-…",
"project": { "id": "9c2d7e41-…", "name": "acme-web" },
"link": { "path": "/home/me/acme-web/.kya-os/project.json", "state": "written" },
"detect": {
"status": "applied",
"report": {
"surface": "pixel",
"outcomes": [{ "outcome": "applied", "description": "edit app/layout.tsx (before </body>)" }],
"notes": ["…"]
}
}
}