Apps

Ask Amp to build and deploy a production server as a workspace app.

An app is a named deployment target owned by a workspace. Each deploy uploads a new bundle and publishes it at the app’s URL. The app keeps a history of past deployments, including the thread each deployment came from.

Deploy From a Thread

Open a thread and ask Amp to prepare and deploy the server. Tell Amp how to build the server if the repository does not make that clear. You can use a prompt like this:

Build and deploy this project as an app. Create a production build that contains every file and dependency the server needs at runtime. Start it with `node server.js`. Make it listen on `0.0.0.0` using the `PORT` environment variable, use `AMP_DEPLOYMENT_URL` as its public URL, and use `/health` for the health check.

Amp builds the application in the thread’s Orb, prepares a directory with the production output, and runs amp apps deploy. The deployment environment does not install packages or run a build. The directory must already contain the server and its production dependencies. Native dependencies must support Linux on x86-64.

Your server must:

  • listen on the port in the PORT environment variable
  • listen on 0.0.0.0, not only localhost
  • provide an HTTP health endpoint

Runtime Environment

Amp sets these environment variables when it starts the server:

  • PORT is the port passed to amp apps deploy. The server must listen on this port.
  • AMP_DEPLOYMENT_URL is the public URL for the app and includes a trailing slash. It is the selected custom URL when you pass --domain. Otherwise, it is the Amp URL at <app>--<workspace>.onamp.dev. Use it when the server needs its public origin for an absolute URL, redirect, or callback.
  • NODE_ENV is set to production.

Amp does not set HOST. Configure the server to listen on 0.0.0.0 in its code or start command. Do not use the local health check address as the public origin.

Secrets and Environment Variables

Use amp secrets with --app workspace/app. Pass values through a file, or use --data-file - to read from standard input.

amp secrets set NAME --app workspace/app --secret --data-file ./secret.txt
amp secrets set NAME --app workspace/app --env --data-file ./value.txt
amp secrets list --app workspace/app
amp secrets history --app workspace/app
amp secrets delete NAME --app workspace/app --yes

App values override workspace values with the same name. Redeploy the app after changing a value.

Deploy From the CLI

You can also run amp apps deploy yourself. Pass the app as workspace/app, the directory to upload, and the executable that starts the server. Deploying to an app name that does not exist yet creates the app. App names contain only letters, numbers, and hyphens, and can be up to 29 characters. The executable can be a command included in the deployment runtime, such as node, or an executable file in the uploaded directory. An uploaded executable file must have execute permission. For a directory whose entry point is server.js:

amp apps deploy workspace/app ./dist \
  --command node \
  --arg server.js \
  --port 3000 \
  --health-path /

Repeat --arg for each argument passed to the executable. Amp creates a compressed archive that contains every file and directory inside ./dist, then uploads it. Deployment bundles can be up to 250 MiB. Amp starts the executable and checks the health endpoint. A failed deployment does not replace the active deployment. Pass --size d1.small or --size d1.large to choose the sandbox size for a deployment; without --size, the app’s default size is used.

List a workspace’s apps with amp apps list <workspace>, show an app’s deployment history with amp apps deployments <workspace>/<app>, and delete an app and its deployments with amp apps delete --yes <workspace>/<app>.

Obelisks

An obelisk is an app’s single long-lived sandbox. Instead of uploading a bundle into a new sandbox on every deploy, the obelisk keeps one sandbox with the app’s Git repository checked out, so processes, databases, and files on disk survive deploys. Deploying pulls the newest commit into that checkout. Obelisks are experimental and live alongside bundle deploys; the amp apps obelisk-* commands and the Obelisk section in workspace settings appear only for accounts they are enabled for.

Deploy the current repository’s origin remote, or pass --repo:

amp apps obelisk-deploy <workspace>/<app>
amp apps obelisk-deploy <workspace>/<app> --repo https://github.com/<owner>/<repo> --branch main --port 3000

The first deploy creates the sandbox and clones the repository into /home/user/workspace/app, using your connected GitHub account for GitHub repositories or your Amp account for Amp-hosted repositories such as https://ampcode.com/@<workspace>/<repository>. Later deploys fast-forward that checkout; a diverged or dirty checkout fails the deploy instead of being reset. The CLI drives the deploy and streams its output, so you can stop it with Ctrl-C.

Define how the app builds and runs in .amp/obelisk.yaml in the repository:

build: pnpm install --frozen-lockfile && pnpm build
services:
  web:
    command: node start.js

After the Git sync, a deploy runs build in the checkout and fails if it exits non-zero. Each entry under services becomes a systemd unit named amp-obelisk-svc-<name> that runs the command in a login shell as the user account with the app’s environment variables (including PORT and AMP_DEPLOYMENT_URL), restarts when it exits, and starts again when the sandbox restarts. A deploy installs and restarts every service, removes units for services the file no longer defines, and then waits for the app to accept connections on its port. A service may set cwd (relative to the checkout) and extra env variables. Without .amp/obelisk.yaml, a deploy only syncs the repository and leaves the app’s processes alone.

Run short commands (up to 60 seconds) in the checkout with the app’s environment variables loaded, for example to restart a service or read its logs:

amp apps obelisk-exec <workspace>/<app> 'git log -1 --oneline'
amp apps obelisk-exec <workspace>/<app> 'sudo systemctl restart amp-obelisk-svc-web'
amp apps obelisk-exec <workspace>/<app> 'sudo journalctl -u amp-obelisk-svc-web -n 50 --no-pager'

Each obelisk runs an Amp runner named <app>-obelisk in the checkout, so threads can be started on the production app itself with the list_runners and create_thread tools. Show the obelisk’s status, sandbox, commit, and runner with amp apps obelisk <workspace>/<app>; the workspace Apps page shows the same information.

An app has one backend. Once an obelisk deploy succeeds, the app’s URL routes to the obelisk, its bundle deployments are retired and their sandboxes killed (they stay in the deployment history), and amp apps deploy refuses the app until the obelisk is deleted. Delete an obelisk and everything stored on its sandbox with amp apps obelisk-delete --yes <workspace>/<app>; the app’s URL stops resolving until it is deployed again. Deleting the app also kills the obelisk sandbox.

Build an App With the Skill

The quickest way to a running obelisk app is the building-amp-apps skill. It gives Amp a starter app with Sign in with Amp, the Jellyware widget, and a SQLite database already wired up, plus the exact commands to create, deploy, and change the app. In a thread with no project, use a prompt like this:

Use the building-amp-apps skill to create and deploy a new app named guestbook in the acme workspace.

Amp then:

  1. Copies the skill’s template into a new Git repository and runs its checks. The template is a SvelteKit app with no runtime dependencies; the database comes from node:sqlite.
  2. Runs amp projects create --push so the code lives in an Amp-hosted repository, and amp apps create <workspace>/<app> to reserve the app and its URL.
  3. Registers a Sign in with Amp client for https://<app>--<workspace>.onamp.dev/auth/callback and stores AMP_OAUTH_CLIENT_ID, AMP_OAUTH_CLIENT_SECRET, and a generated APP_SESSION_SECRET as app variables with amp secrets set --data-file. The secrets are never shown.
  4. Sets AMP_APP in .amp/obelisk.yaml, pushes, and runs amp apps obelisk-deploy once.
  5. Checks /health, the anonymous redirect to /login, and the service log, then asks you to sign in at the app URL and confirm the Jellyware button appears.

The whole flow takes about five minutes. Afterwards you change the app either by asking Amp to edit, push, and redeploy, or from the running app through the Jellyware widget. The widget’s Production destination runs the agent on the app’s own obelisk and is offered only to the user who ran the last deploy.

The skill needs an account with apps and obelisks enabled and, to register the OAuth client itself, the create-oauth-client-apps flag with workspace admin access. When that last part is missing, the skill explains how to create the client in workspace settings and store the credentials by hand. It currently ships as a workspace skill in the amp workspace; amp skills list shows whether it is available to you.

Jellyware Widget

An obelisk app can include the Jellyware widget so its creator and workspace admins can select page elements and send requests to Amp from the deployed app. The app must use Sign in with Amp. An app login does not grant permission to run agents or change production data.

From your app’s server, use the signed-in user’s OAuth access token to check widget access:

GET https://ampcode.com/api/jellyware/context?app=workspace/app
Authorization: Bearer <OAuth access token>

The token must be issued for the https://ampcode.com/api/v2 resource. Keep it on your server. Do not put it in HTML, browser storage, or script attributes. This endpoint checks the current app creator and workspace membership. Repository write access is not app ownership.

The response is { "widget": null } when the user cannot use the widget. Otherwise, widget contains scriptURL and appID. Render the following only for an allowed user, using those values:

<script
	src="https://ampcode.com/jellyware/widget.js"
	data-jellyware-app="APP_ID_FROM_CONTEXT"
	async
></script>

If the access check fails or is unavailable, leave the widget hidden and keep the app usable. Single-page apps must call window.__ampJellyware?.setEnabled(false) when removing the integration or when access is lost. Remove any pending script element too, and disable the widget if that script finishes loading after cleanup. Loading the script again restores an existing widget without adding a second copy. The user’s choice to hide the review button is preserved.

Send opens an Amp confirmation page. Your comment and selected element are shown before any agent starts. Choose either:

  • Orb. Create a thread in a project matching the app’s repository. Ask for deployment in your comment if you want Amp to deploy the resulting code.
  • Production. Create a thread on the live app runner, with access to its database and files. This is available only to the user who deployed and owns that runner, while the matching runner is online. Workspace admin access alone does not grant production access.

Amp checks these permissions again when you confirm. Follow the created thread for progress or questions. After a requested deployment finishes, return to the app and reload it. The widget keeps unsent comments if the confirmation window is blocked or closed. Do not resend a request whose outcome is uncertain without checking your Amp threads first. Dictation and portal diagnostic uploads are not available in the production widget.

Inline confirmation

Apps can show the destination picker and confirmation inside the widget instead of a separate page. Set data-jellyware-inline="true" on the widget script:

<script
	src="https://ampcode.com/jellyware/widget.js"
	data-jellyware-app="APP_ID_FROM_CONTEXT"
	data-jellyware-inline="true"
	async
></script>

If your app sends a Content Security Policy, allow frame-src https://ampcode.com. Nothing else changes on the app side: keep the context check, and do not add an app endpoint that talks to Amp for the user. The app’s OAuth token is never used to start work.

On Send, the widget embeds an Amp page from https://ampcode.com in its tray. The first time, the user connects it to their Amp account in a small Amp window; this is a normal Amp sign-in on the Amp origin and stays connected for 12 hours in that browser. The embedded page then shows the comments, the destination choice, and a Start in Orb or Send to Production button. Amp checks app access, the project, and the runner when the page loads and again on send. Nothing starts until the user clicks that button in the Amp page. Every send is confirmed separately; there is no standing permission to grant or revoke.

The tray keeps an Open in Amp link to the full confirmation page. Use it if the embedded page does not load or a browser blocks it. A failed inline request keeps comments queued in the widget.

Custom Domains

First add a domain in workspace settings under Domains. Create the DNS records shown there and wait until the domain is Active. The app’s workspace and the custom domain must have the same owner.

To use only direct subdomains (for example, app.example.com), omit the routing record for the root domain and only add the other records shown. You can leave example.com pointed at its current service.

Use the configured domain itself or one direct subdomain:

amp apps deploy workspace/app ./dist \
  --command node \
  --arg server.js \
  --domain app.example.com

Amp checks ownership and domain status when publishing and again when the deployment becomes active. The custom hostname changes only after the new deployment passes its health check. Later deployments keep the current custom hostname when you omit the domain options. Pass --default-domain to remove the custom route after the next deployment becomes active.

The Amp URL at <app>--<workspace>.onamp.dev stays available when a custom domain is active.

Access and Pausing

Apps are public. Anyone with the URL can use an app without signing in to Amp. The deployed server must enforce any authentication, CORS, or CSRF rules that it needs.

Workspace members can create, update, deploy, and delete apps and view their deployment history.

Apps pause when inactive and resume when they receive a request. The first request after a pause can take longer while the app resumes. Any HTTP request can wake it, and continued requests can keep it running longer.

When a person opens an app inside Amp, a link with target="_amp" can open an Amp page in the containing Amp app:

<a href="https://ampcode.com/threads/T-..." target="_amp">Open the thread in Amp</a>