Applet CLI
Create, deploy, and manage apps with Applet.
Install
npm install -g @applet-one/cli
Requires Node.js 20 or newer. The CLI and its Miniflare/workerd runtime are the only requirements for Applet development and deployment; pnpm is only used to run the starter scripts. Wrangler is optional for direct Cloudflare deployment.
Quick start
applet init my-app
cd my-app
pnpm install
pnpm dev
To scaffold into the current directory instead, run applet init . from a folder where the starter's files do not already exist; the folder name becomes the app name.
The starter is a single-class app: app.js exports class App extends DurableObject and serves every request; Applet supplies the Worker configuration (binding APP, migration, default handler, backup endpoint). It is the same shape an agent builds in chat through the Applet MCP server, so a project can move between the terminal and chat. Projects with their own wrangler.jsonc keep working as before.
Sign in and put it live:
applet login
applet push # save a hosted draft for chat editing; does not publish
applet deploy # or: build and deploy from here
Chat and the CLI on the same app
applet pull my-app # download an app built in chat into an empty directory (links it)
applet push # send the project's source files back; refuses when the app was edited in chat since the last pull
applet pull # fetch the edits an agent made; refuses to overwrite local files that differ (--force)
applet deploy # build/publish locally; no editable source upload
applet deploy --sync-source # explicitly sync source, startup-check it and publish
The link lives in .applet/link.json. Its existing app_id is authoritative: linked commands resolve the current app name/workspace by ID, so renames do not retarget the project. Cached app_name is display metadata; an ID-only link also works. Deleted or inaccessible IDs fail without name fallback or automatic replacement, even with --force. Malformed links fail closed. An explicit app name or configured workspace that conflicts with the linked target is rejected; use a separate directory for another app. API-origin binding and a relink command are not part of this iteration. No link-file migration is required; source revisions and per-file sync baselines are preserved.
Push accepts dependency-free projects only (relative imports; the sandbox runs files as they are); projects with npm dependencies stay on applet deploy. Apps deployed with the CLI and never pushed are listed in chat as not editable.
Optional hosted browser preview
When the service has browser preview configured, applet push prints a Private preview link separately from the live app URL. deploy --sync-source --json includes the returned preview metadata without a separate preview request. Local-only deploy still does not upload editable source or provision a hosted preview; use local tooling to try local changes.
The hosted preview is pinned to a source revision, requires browser sign-in, and shares sandbox test data—not live data. Old links conflict after source edits. Availability does not prove startup/publication; opening it never publishes, and publication never requires a preview/approval visit. Previews use reserved sibling hostnames on applet.works, with host-only platform sessions; sibling origins are not separate cookie sites. No URL is advertised until the coordinated preview routing rollout is enabled.
Custom app URLs (Pro)
An administrator can enable custom URLs for your account; contact us to request access. From the existing app's project directory:
applet url # canonical URL and any pending change
applet url check sunny-weather # advisory availability; claims nothing
applet url set sunny-weather # interactive confirmation
applet url set sunny-weather --yes # explicit noninteractive confirmation
Names are normalized to lowercase and must contain two or more words separated by hyphens, each with at least 3 ASCII letters (no digits), with a maximum of 32 characters total. For example, sunny-weather or bread-and-bot. Existing URLs remain unchanged; setting the current name is still a no-op. Platform names and names claimed by other apps are unavailable. The app must already exist remotely; choosing a name does not create or deploy it. An undeployed app's URL becomes reachable after its first successful deployment.
The previous URL stops working without a redirect, and its name can immediately be used by another app. Old bookmarks, embeds, webhooks, and authentication callbacks may then reach that other app. Returning visitors may also expose cookies or browser storage associated with the old origin to its new owner. Browser storage, cookies, and sign-in do not transfer to the new origin. Update integrations and links deliberately.
This changes only the hosted public address—not the project name, immutable app identity, state, or secrets. It writes no local files and adds no applet.jsonc setting. Redeployment preserves your chosen URL. Revoking the capability blocks new changes but keeps existing URLs active.
The CLI waits up to one minute for accepted operations. On an incomplete or uncertain outcome, run applet url to inspect the canonical URL and pending operation. Closing/cancelling a client does not roll back the server operation. Metadata-only recovery runs periodically; do not assume failure and claim another name.
Deploy code and secrets together
applet deploy --secrets-file /secure/path/secrets.json
# Or a dotenv file (Node.js 20.12+):
applet deploy --secrets-file /secure/path/credentials.env
This works for the first deployment as well as redeployment. A .json file must be a UTF-8 object mapping names to string values; other extensions use dotenv parsing. JSON preserves strings exactly; dotenv quoting rules apply. Use a protected file outside your source tree and never commit it. A file must contain 1–100 secrets, with non-empty values up to 5 KiB UTF-8 each and a total JSON payload below 1 MiB. Tokens require both deployments:write and apps:write.
Code and supplied secrets go into the same Cloudflare publication. Supplied names replace current values; omitted secrets are retained, not deleted. Values are sent through a synchronous authenticated request, never placed in the artifact, queue, database, audit record or CLI output. Importing the selected secrets file into Worker code is rejected. There are no automatic value-bearing retries. If publication is interrupted or its outcome is uncertain, inspect deployment status and verify the integration before deliberately retrying with the intended current credentials. This does not guarantee immediate refresh of already-running Durable Objects.
Deployed app secrets
applet secrets set API_KEY # TTY prompt, echo disabled
applet secrets set API_KEY --stdin # exact UTF-8 stdin, including trailing newline
applet secrets list --json # sorted names/types only
applet secrets delete API_KEY --yes # explicit noninteractive confirmation
Run from the app's project directory; no build/upload or Cloudflare login is needed. Values are never accepted as arguments, flags, or environment variables. For shell automation, use printf rather than echo when a trailing newline is not intended; pipe from a secure source instead of typing a real value in shell history. --no-input requires --stdin for set or --yes for delete. Mutations also support metadata-only --json.
Names must match [A-Za-z_][A-Za-z0-9_]*, max 128 characters; APPLET_/__APPLET_ prefixes and collisions with vars/DO bindings are reserved. Values must be non-empty and at most 5 KiB UTF-8. Whitespace/Unicode are preserved. Tokens with apps:write can change credentials; apps:read can list names.
For standalone secret commands, deploy code that safely handles missing env.API_KEY, then set it and enable/use the integration. Never-deployed apps return APP_NOT_DEPLOYED; use applet deploy --secrets-file PATH to supply credentials during the first publication instead. Cloudflare retains values; there is no get-value command. Code can read/exfiltrate its own secrets and printing them can leak them to app logs—there is no automatic redaction or end-to-end encryption.
For local applet dev, put dotenv KEY=value entries in git-ignored .dev.vars (Node 20.12+). Local entries override ordinary vars, never a DO binding. This file is not automatically read by build/deploy and never enters artifacts. You can explicitly publish its values with applet deploy --secrets-file .dev.vars. Never put credentials in vars/config/source or commit .dev.vars.
Publication is designed to retain current secrets; app deletion removes the underlying script/bindings. Live rotation/deletion does not promise instantaneous global propagation. Existing Durable Object instances can retain an older constructor environment even while fresh objects see updated bindings; verify the actual integration and revoke credentials at their issuing service when needed. On SECRET_OUTCOME_UNCERTAIN, retry deliberately with the intended current value: a delayed retry can overwrite a newer rotation. There are no background value-bearing retries. Publication always requests preservation. See the app secrets guide in the dashboard for the full workflow, local development and security guidance.
Known issue: existing Durable Objects may not refresh secrets immediately
Status: open. A successful secret update acknowledges configuration publication, not immediate refresh of every running object. Existing Durable Objects can retain their earlier constructor environment: a newly added secret may remain unavailable, and a rotated or deleted secret may remain usable by running code.
In production testing, an existing object kept its old environment throughout three minutes of polling while fresh objects received updated bindings. Three minutes is an observation window, not a refresh deadline. We have not established a maximum refresh time or a guaranteed immediate-refresh workaround. Do not assume that redeploying code forces existing objects to refresh.
For first startup, use applet deploy --secrets-file PATH to publish code and credentials together; this does not guarantee refresh of existing objects. Verify the real integration after changes. To invalidate an old credential, revoke it at the issuing service—removing a Cloudflare binding does not erase copies already held by running code.
Publication provenance and source selection
CLI 0.4.5+ records artifact SHA-256 and optional Git commit/dirty state captured at workflow start. Git metadata is a trace, not proof of source/artifact equivalence. deploy --json distinguishes claimed and server-verified source provenance. Linked status --json includes linked_deployment canonical metadata/provenance without an extra discovery call; it does not claim live HTTP verification. Local-only publication leaves hosted-source equivalence unknown even if a revision was previously synchronized.
push and deploy --sync-source select files once. Selection honors root/nested .gitignore rules and root .appletignore overrides, excluding dotfiles, dependencies, build output, symlinks, common credential/secret JSON filenames and the supplied secrets file. Protected exclusions cannot be undone with ignore negations. Required configurations, entry points or imports that are excluded/unsupported fail preflight before avoidable remote writes. This is a sharing policy, not a universal secret scanner: never embed credentials in otherwise included files. The CLI reports the selected count/bytes; structured deploy results include the selected paths. Global Git excludes and .git/info/exclude are not applied; keep sharing exclusions in project .gitignore/.appletignore.
Publication records the inspected live deployment as its base. If another publication wins first, LIVE_DEPLOYMENT_CHANGED rejects the stale request under the app coordinator; inspect/reconcile rather than blindly retrying. Hosted publication after local-only deployment requires synchronization (CLI: deploy --sync-source) or explicit MCP replacement scoped to expected_live_deployment_id with replace_live: true. This is an operation parameter, not a mandatory extra approval/preview step when replacement is already authorized. Source conflicts are still revision-checked; do not use blanket force as a substitute for merging.
Finalized artifact bytes are stored outside the 15-minute staging PUT capability. Downloads verify the recorded digest, including legacy artifacts; ARTIFACT_INTEGRITY_ERROR does not alter live code/data. Server preparation never queues or persists secret values: combined sync plus --secrets-file finalizes the prepared artifact through synchronous credential publication. Startup checks still receive no platform live secrets; apps must start safely without those credentials in the sandbox.
Upgrade requirement: the new finalization contract requires a publication base. Older clients/legacy queued work without one fail closed with PUBLICATION_BASE_REQUIRED; use CLI 0.4.5+ and inspect before preparing a fresh publication. Existing live deployments, source, state, secrets and links are retained.
Commands
-
applet init <project-name|.>— create a starter app (app.jswithclass App, private access);.scaffolds the current directory (using its folder name as the app name) if the generated paths are unused. -
applet pull [app-name] [--force]— download the app's source files into the current directory and link it; linked projects resolve the immutable app ID; otherwise the name defaults to the directory name. Addspackage.json,README.mdand.gitignoreto a single-class app when they are missing. -
applet push [app-name] [--publish [--wait]] [--force]— upload source files (.js .mjs .ts .md .json .jsonc, excludingnode_modules, build output and local state) as a draft, creating the app when needed. It does not publish by default.--publishexplicitly publishes the startup-checked revision;--waitrequires it and waits up to 25 seconds (exit 2 if pending; exit 1 on reported startup/publication failure). Refuses after edits in chat since the last pull and refuses npm imports. -
applet migrate-config— split a legacy combinedapplet.jsoncinto standardwrangler.jsoncand Applet-only hosting settings. Preserves the original asapplet.legacy.jsonc; refuses conflicting Wrangler files or an existing backup. Local only; no login or deployment needed. -
applet version/applet --version— print the installed CLI version. -
applet login— authenticate with Applet. -
applet logout— sign out and remove the saved login. -
applet status [--json]— check authentication status;--jsonemits a machine-readable result. -
applet dev [--host HOST] [--port PORT]— run the Worker locally on Miniflare/workerd; local Durable Object state persists under.applet/dev. Restart the command after source changes. -
applet build --dry-run— validate configuration and build an artifact summary without uploading. -
applet deploy [--sync-source] [--secrets-file PATH] [--json] [--wait-ms 0..120000]— validate/build and explicitly publish the local project, optionally with credentials. No editable source upload or automatic linking, even for linked projects.--sync-sourceopts into selected source upload/linking, hosted startup validation and publication of the server-built artifact of that exact snapshot (not a separately built local bundle); conflicts or startup failures prevent publication (a successfully uploaded draft remains saved). This shortcut supports dependency-free source; ordinary artifact deployments support bundled npm dependencies. The supplied secrets file is never pushed. Existing app access is preserved; new apps default private unless hosting configuration explicitly requests public. Useapplet accessto change an existing audience. Legacy combined configurations still work.--jsonemits one structured result with target, artifact digest, synchronized revision when available, deployment ID, validation facts and outcome; progress goes to stderr.--wait-msbounds publication waiting (default 120000 ms). An expired requested wait reports pending and exits 2;--wait-ms 0reports acceptance without waiting. Pending work continues without resubmission. Build/startup validation is not live functional verification. Local tests can be chained in one shell call, e.g.pnpm test && applet deploy --json; do not claim live correctness until the real integration is verified. Without source sync, hosted-source equivalence is unknown; this does not overwrite hosted drafts or update their local sync baseline. -
applet url [set NAME [--yes] | check NAME]— inspect or explicitly change the hosted public URL; custom changes require account access. No local file changes. -
applet access— show local and live access.applet access privatechanges both JSONC and the live app;applet access public --yesdoes likewise after explicit confirmation. Before first deployment the command changes JSONC only. Opening a private app URL in a browser redirects to Applet sign-in when needed. API requests without an app-host session receive 401; CLI tokens cannot make general runtime requests. -
Direct Cloudflare deployment remains available with Wrangler:
wrangler deploy. Wrangler is not needed for Applet-managed deployment. -
applet logs [--limit N]— show the latest 1–100 deployed-app log entries from the last 24 hours (default 100), in time order. Includes deployment outcomes (deployment:), runtime logs, and dispatch failures; it does not live-tail or showapplet devoutput. A successful deployment creates an entry even before the app handles a request. Deployment entries come from Applet's deployment records, not Cloudflare Workers Logs. Log collection begins on deployment after Applet enables observability; earlier events cannot be recovered. Cloudflare retains Workers Logs for 3 days on Free or 7 days on Paid. Your app's console messages may contain sensitive data; avoid logging credentials or personal information. -
applet backup— download the app's configured state backup. The project must enable top-levelbackupinapplet.jsonc(legacy:applet.backup) and be redeployed after enabling it; exports are currently limited to 10 MiB. -
applet delete [app-name] [--yes] [--no-input]— permanently delete a named hosted app from any directory, or omit the name to use the current project's app. Without--yes, the CLI asks for confirmation;--no-inputdisables prompting and requires--yes.
Worker fields belong only in Wrangler configuration; applet.jsonc contains access, backup, optional workspace, and optional $schema.
Personal and team workspaces
Commands default to your personal workspace, regardless of membership ordering. To select a team created in the dashboard, run from your project directory:
applet workspace list # accessible workspace slugs, types and roles
applet workspace use my-team # persist this project's selection
applet status --json # inspect the selected workspace and apps
workspace list --json returns the accessible workspaces as JSON and works outside a project. workspace use saves "workspace": "my-team" in applet.jsonc (legacy combined files: applet.workspace), preserving comments and other settings. It does not move or deploy apps. Switching an already-linked project to another workspace is rejected to avoid silently retargeting its app; use a separate directory instead. You can select your personal workspace again using its slug. This scopes status, pull/push, deploy, secrets, logs, access, URL, backup, and deletion to that workspace. If access is removed, the CLI fails explicitly; it never falls back to another workspace. For a first pull into an empty directory, create applet.jsonc with the workspace setting before running applet pull APP.
Team editors can create/edit/deploy apps, read logs and manage secrets. Only owners can delete apps, change access/URLs, or take backups; editor deployments must not request an access change. A team membership covers all current and future apps. Editors are trusted code authors: deployed code can access production data and secrets. Files and sandbox data are shared; use one active editor/agent per app because concurrent changes may overwrite each other. Private published browser URLs remain owner-only (and admins). Removing membership blocks the next request, though an already-started publish may finish. Team creation and membership administration require dashboard sign-in, not CLI/bearer tokens.
Without hosting settings, a new app deploys with private access; existing access is preserved. Worker projects have no backups by default; single-class apps enable backups. Direct Wrangler deployments do not enforce Applet private access or include its backup helper. Wrangler TOML and environment resolution are not supported by Applet yet; using Wrangler configuration does not provide full Wrangler feature parity. See docs/applet-project-config-v1.md for capabilities and migration details.
App names are not hostname labels. Use the generated URL printed after deployment or returned by the app API.
Set APPLET_API_URL to use a non-default Applet API endpoint.