End-to-end demonstration of agentcli governing a real deployment pipeline. Provisions infrastructure via Stripe Projects, deploys a Next.js storefront with Stripe payments to Vercel, monitors operations with per-task least-privilege Stripe API keys, and produces a cryptographic proof-of-custody audit trail.
- Multi-workflow manifests with identity profiles, contracts, and evidence
- Compilation to multiple runtime targets (standalone, openclaw-scheduler)
- Identity resolution -- different principals per task, enforced before execution
- Least-privilege identity split for both Stripe (restricted keys) and Vercel (readonly vs deploy)
- Infrastructure provisioning via Stripe Projects (Neon Postgres + Vercel)
- Governed deployment -- every CLI operation wrapped in agentcli with attestation
- Payment monitoring with per-task scoped Stripe restricted API keys
- Full audit trail with SHA-256 command hashes and SSH signatures
| Tool | Version | Purpose |
|---|---|---|
| agentcli | >= 0.2.0 | Manifest governance, identity, execution |
| stripe CLI | latest | Stripe Projects provisioning, test triggers |
| vercel CLI | latest | Deployment (wrapped by agentcli) |
| node | >= 22 | Next.js app runtime |
| jq | >= 1.7 | JSON output formatting |
| curl | any | Health check verification |
| SSH key | ed25519/rsa | Evidence attestation (uses ~/.ssh/id_*) |
| Account | Purpose |
|---|---|
| Stripe (test mode) | API keys, restricted keys, payment triggers |
| Neon | Postgres database (provisioned via Stripe Projects) |
| Vercel | Hosting (provisioned via Stripe Projects) |
Create .env in the repo root with the following keys from your Stripe account (test mode):
STRIPE_API_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_KEY_CHARGES_READ=rk_test_...
STRIPE_KEY_BALANCE_READ=rk_test_...
STRIPE_KEY_PAYMENT_INTENTS_READ=rk_test_...
The three rk_test_ keys are restricted API keys created in
Dashboard > Developers > API keys > Create restricted key, each with a single permission:
| Key | Permission |
|---|---|
STRIPE_KEY_CHARGES_READ |
Charges: Read |
STRIPE_KEY_BALANCE_READ |
Balance: Read |
STRIPE_KEY_PAYMENT_INTENTS_READ |
PaymentIntents: Read |
The webhook secret comes from stripe listen --print-secret.
Additional credentials (DATABASE_URL, VERCEL_TOKEN) are provisioned automatically
by Stripe Projects during Part 4 of the walkthrough.
agentcli-demo/ # this repo (github.com/amittell/agentcli-demo)
manifest.json # agentcli manifest (4 workflows, 5 identities)
walkthrough.sh # automated demo script
walkthrough.md # manual step-by-step guide
manifests.md # plain-English manifest reference
README.md # this file
FUTURE-WORK.md # recording TODOs
.env # Stripe keys (manual, gitignored)
next.config.js # Env passthrough (DATABASE_URL, STRIPE_* to runtime)
app/ # Next.js 15 App Router pages
page.js # Storefront product grid
admin/page.js # Admin dashboard (charges + balance)
orders/page.js # Order history
checkout/ # Success + cancel pages
api/
checkout/route.js # Stripe Checkout session creation
health/route.js # Health check (DB + Stripe)
orders/route.js # Order list API
webhooks/stripe/route.js # Webhook handler (signature verified)
components/
buy-button.js # Client component for checkout
nav.js # Shared navigation
lib/
db.js # PrismaClient (standard connection, no adapter)
stripe.js # Stripe SDK initialization
prisma/
schema.prisma # Product + Order models
seed.js # 4 demo products
migrations/ # 0001_init
agentcli-demo/ # Created by stripe projects init (gitignored)
.env # Stripe Projects credentials (auto-pulled)
.projects/ # Stripe Projects state
The manifest defines 5 identity profiles with least-privilege separation:
| ID | Provider | Principal | Trust | Purpose |
|---|---|---|---|---|
project-admin |
none | agent://demo/project-admin |
supervised | Stripe Projects provisioning |
stripe-ops |
stripe-api-key | agent://demo/stripe-ops |
restricted | Payment monitoring (3 scoped keys) |
database-admin |
env-bearer | agent://demo/database |
supervised | Database migrations |
vercel-readonly |
env-bearer | agent://demo/vercel-readonly |
restricted | Deployment inspection, health checks |
vercel-deploy |
env-bearer | agent://demo/vercel-deploy |
supervised | Deploy writes, credential sync |
The stripe-ops profile uses the stripe-api-key provider with precreated restricted keys.
Each task gets a different key scoped to only the Stripe resources it needs:
| Scope | Permission | Key Env |
|---|---|---|
charges_read |
Charges: Read | STRIPE_KEY_CHARGES_READ |
balance_read |
Balance: Read | STRIPE_KEY_BALANCE_READ |
payment_intents_read |
PaymentIntents: Read | STRIPE_KEY_PAYMENT_INTENTS_READ |
The Vercel identity is split into vercel-readonly (restricted, read-only inspection)
and vercel-deploy (supervised, deployment writes). Same least-privilege pattern as Stripe.
| ID | Name | Tasks | Identity | Purpose |
|---|---|---|---|---|
provision |
Provision Infrastructure | 4 | project-admin | init, add neon, add vercel, pull credentials |
deploy |
Deploy Application | 7 | mixed | sync creds, migrate, deploy, inspect, verify health, verify payments |
stripe-ops |
Stripe Payment Operations | 3 | stripe-ops (scoped) | list charges, check balance, list failed payments |
cleanup |
Teardown Demo Environment | 1 | vercel-deploy | remove Vercel project |
All tasks produce SSH-signed evidence binding the execution ID, declared identity, command, and result into a canonical JSON payload. This creates an unforgeable chain tying every operation to the principal that ran it.
Each workflow and task can declare a contract specifying:
sandbox: execution isolation levelnetwork: network access policyaudit: whether the execution must be auditedrequired_trust_level: minimum trust level for the task's identitytrust_enforcement:advisory(warn) orstrict(fail if violated)
The deploy workflow uses strict enforcement on deploy-app and run-migrations,
meaning agentcli will refuse to execute them if the identity's trust level is insufficient.
# Full run -- provisions, deploys, transacts, audits
./walkthrough.sh
# Full JSON output (verbose mode)
./walkthrough.sh --verbose
# Skip provisioning (infra already up)
./walkthrough.sh --skip-provision
# Skip provisioning and deployment (app already live)
./walkthrough.sh --skip-provision --skip-deploy
# Tear down everything for a clean re-run
./walkthrough.sh --cleanupThe script is idempotent -- re-running skips already-provisioned resources. Cleanup is comprehensive: Stripe Projects services, Vercel project (via agentcli), local state, and audit log.
See walkthrough.md for a step-by-step guide with every command.
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| UI | React 19 + Tailwind CSS v4 |
| Database | Neon Postgres (via Prisma, standard connection) |
| Payments | Stripe Checkout (redirect flow) |
| Hosting | Vercel |
| Provisioning | Stripe Projects |
| Governance | agentcli v0.2 |
"Interactive prompt rendering unavailable" when adding Vercel
: The Vercel OAuth flow requires a terminal. Run the command directly:
stripe projects add vercel/project --accept-tos --yes --config '{"name":"agentcli-demo"}'
"Project already exists" on Vercel add
: A previous Vercel project with that name still exists. Run cleanup first:
./walkthrough.sh --cleanup
Identity resolution fails for deploy tasks
: Credentials not loaded. Run stripe projects env --pull from the
agentcli-demo/ subdirectory, then source the .env.
Trust level mismatch on run-migrations
: The database-admin identity must have supervised trust to match the
run-migrations contract's required_trust_level: supervised.
NEON_CONNECTION_STRING vs DATABASE_URL
: Stripe Projects pulls credentials as NEON_CONNECTION_STRING. The walkthrough
script aliases it to DATABASE_URL in load_env(). For manual runs, export it:
export DATABASE_URL="$NEON_CONNECTION_STRING"
Stripe Projects vault sync on sandboxes
: stripe projects env --pull may skip resources and leave .env empty on sandbox
accounts. The walkthrough script has a 3-tier fallback: (1) normal pull, (2) re-add
services to force credential sync, (3) API debug extraction via --debug flag.
Vercel runtime env vars
: Vercel serverless functions do not load .env files at runtime. Env vars must be
set in Vercel project settings using vercel env add (requires vercel login with
personal auth, not the Stripe Projects vca_ token). The next.config.js env
passthrough handles build-time injection as an alternative.
Prisma/Neon adapter on Vercel
: The @prisma/adapter-neon driver adapter has initialization timing issues on Vercel
serverless. Use standard PrismaClient with the Neon connection string directly --
it works as a standard Postgres URL. The demo uses this approach in lib/db.js.
Vercel team OAuth linking : Each Vercel team can only be linked to one Stripe account. If you get "Team is already linked to a different Stripe account", disconnect Stripe from the old account in Stripe Dashboard > Installed Apps.