Skip to content

feat(samples): add self-improving Mastra demo for AWS and GCP#648

Merged
lionello merged 38 commits into
mainfrom
feature/self-updating-mastra
Jul 21, 2026
Merged

feat(samples): add self-improving Mastra demo for AWS and GCP#648
lionello merged 38 commits into
mainfrom
feature/self-updating-mastra

Conversation

@raphaeltm

@raphaeltm raphaeltm commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

What

Adds a self-updating Mastra demo with a public production app and a gated,
always-on development environment. The coding agent edits in an isolated git
worktree, typechecks, and applies successful changes atomically; the admin can
review history, reboot to the published image, and explicitly publish from
inside the dev container.

Cloud deployments share one compose.yaml; compose.dev.yaml is only for
local Docker development. The two committed stacks select non-secret env files:

Stack Interpolation file Publish permission Model gateway
aws .env.aws AdministratorAccess chat-default, 2 GiB
gcp .env.gcp roles/owner chat-default, 512 MiB

.defang/aws and .defang/gcp use the merged COMPOSE_ENV_FILES stack
support. Secrets remain in Defang config, and cloud credentials come from the
dev workload's ambient identity.

Cloud behavior

  • A private host port on dev keeps the editable GCP workspace on one Compute
    Engine VM instead of a scale-to-zero Cloud Run service. It remains private on
    AWS and does not create another public endpoint.
  • x-defang-policies is cross-cloud: the selected env file supplies the AWS
    managed-policy ARN or GCP project role used by admin-triggered self-publish.
  • Self-publish reuses the selected stack, so provider, env file, and scalar
    settings stay together.
  • The explicit model-provider service lets AWS use the field-tested 2 GiB
    gateway while GCP uses 512 MiB.

Verification

  • current-head Defang stack + Compose loader/fixup render:
    • default: publishing disabled, chat-default, 1 GiB validation fallback
    • AWS: provider aws, 2 GiB gateway, full AWS managed-policy ARN
    • GCP: provider gcp, 512 MiB gateway, roles/owner
  • exact file-layout assertion: only compose.yaml and compose.dev.yaml
  • no stale cloud-overlay references
  • npm run typecheck in agent/
  • git diff --check
  • independent read-only elegance review: PASS
  • prior GCP E2E: all four services served and a live LiteLLM completion returned
    200
  • AWS E2E deployment zso8kk4t6qmw: clean four-service deployment; app and dev
    health endpoints returned 200 and /admin remained gated

The current single-Compose head has exact loader coverage for both stacks. A
fresh live deployment of this final consolidation remains to be run on each
cloud.

Samples Checklist

✅ All good!

raphaeltm and others added 10 commits July 16, 2026 07:45
Production app (Cloud Run) + live-editable dev service (forced onto
Compute Engine via second host-mode port), shared managed Postgres, and
a Vertex AI model service for the Mastra coding agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r glue

Hono server (127.0.0.1:4111, container-internal only) hosting a Mastra
Agent with Workspace file tools path-scoped to todo-app/. Runs are
fire-and-forget with a polled log, and a deterministic tsc --noEmit
verify (with one repair round) runs outside the agent's control.
Caddy fronts the Next dev server and absorbs its restarts; entrypoint
git-inits a runtime baseline so agent edits have history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ibility

pg 8.22+ treats sslmode=require as verify-full and rejects Cloud SQL's
internal CA (UNABLE_TO_VERIFY_LEAF_SIGNATURE). Let pg derive TLS behavior
from the connection string instead of overriding ssl in code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ma init

- Better Auth: trust proxy headers and disable the origin check for demo
  mode — the TLS-terminating load balancer made login fail with 'Invalid
  origin'. Marked TODO(security) for the pre-release security pass.
- Serialize ensureSchema() with a pg advisory lock: app + dev racing
  CREATE TABLE IF NOT EXISTS on a fresh DB hit duplicate
  pg_type_typname_nsp_index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… runs on model error

gemini-3.5-flash is not a real Vertex publisher model, so the coding agent's
Vertex call was rejected by the vertexai.allowedModels org policy. Switch to
gemini-3-flash (an allowed, existing model).

Also surface model/stream errors as failed runs: previously a model error was
logged but the run still typechecked (a no-op, since nothing changed) and
reported 'Changes are live', masking the failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erved in europe-west2)

gemini-3-flash returns 404 NOT_FOUND in europe-west2. gemini-2.5-flash is GA
there and validated end-to-end: the coding agent edits app source and the
change hot-reloads live. Keeps the London-demo europe-west2 setup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@raphaeltm
raphaeltm had a problem deploying to deploy-changed-samples July 17, 2026 11:50 — with GitHub Actions Failure
…rver

The coding agent edits todo-app/ and can break the Next.js dev app, including
the admin page used to send fixes to the agent — a deadlock. Move the admin
console out of Next.js into the agent server (Hono), which lives outside the
agent's editable tree:

- agent server now serves /admin (console), /admin/login (break-glass),
  /admin/dispatch, /admin/runs/:id; reads feedback + validates admins directly
  against Postgres (pg added to the agent).
- Caddy routes /admin* to the agent (:4111), everything else to Next.js (:3001),
  so the console survives a Next.js crash.
- Auth: reuse the better-auth admin login (session validated against Postgres,
  works while the app is down) + an ADMIN_TOKEN break-glass fallback.
- Remove the old Next.js admin page, /api/agent/* routes, and admin-console
  component so nothing admin-related remains in the agent-editable tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 12:10 Failure
…guardrail

Deploy 1 of the trustworthy self-updating app:

Run history + model + UX fix (agent server):
- Persist every run to Postgres (agent_runs): status, full log, model used,
  timestamps; survives restarts and redeploys.
- Admin console gains a 'Recent runs' list (click to view a past run's log +
  model), client-rendered feedback + runs that refresh after a run — the run
  log no longer vanishes with nothing to show.
- Runs record which model was used (CHAT_MODEL).

Automatic guardrail (per product decision: auth-only for now, no approvals):
- beforeToolCall hook hard-blocks the agent writing/editing/deleting
  lib/auth.ts — enforced in code, not just the prompt.

Error capture -> backlog (dev and prod):
- Server errors (instrumentation onRequestError) and client errors
  (window listeners + error boundaries) are recorded as 'error' feedback
  items the admin can dispatch to the agent. Deduped and length-capped.
- feedback gains a 'source' column; user_id nullable for system errors.
- Friendly error boundaries (error.tsx, global-error.tsx) that report and
  offer retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 13:00 Failure
@lionello lionello changed the title Feature/self updating mastra Feature/self-updating-mastra Jul 17, 2026
…demand verdict

Every successful agent run now commits its edits (scoped to todo-app/) with
Run-Id/Feedback-Id/Model trailers pointing at the Postgres rows it addressed;
failed runs restore the working tree to the last good commit. Runs are
serialized (409 on concurrent dispatch). The admin console gains a History
panel (commits cross-linked to runs), an admin-only revert-as-new-commit with
a post-revert typecheck report, and an on-demand 'Grade this run' verdict.

Durable history plumbing for the upcoming self-publish: .git is no longer
dockerignored (it rides the build context into the next dev image, under the
100 MiB limit since heavy dirs are gitignored) and the entrypoint keeps an
existing repo instead of re-initializing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 16:47 Failure
…blish interactive login

The Publish panel has the dev container run 'defang compose up --detach' on
its own Compose project, overwriting both the dev and app services from the
live workspace — intentionally no GitOps. Fabric authorization is a fresh
interactive 'defang login' per publish: the console surfaces the auth URL in
a new tab (the CLI's redirect goes to the auth server and it long-polls for
the code, so no callback to this container is needed) and the admin must
complete it every time; the token lives in an ephemeral state dir wiped when
the publish ends. GCP credentials are the dev service's own service account
(deploy roles granted declaratively via x-defang-roles) through the metadata
server — no key files anywhere.

Publishes are recorded in a deployments table with streamed CLI logs and a
publish marker commit (Deployment-Id trailer, authored as the admin); on
boot the next container generation marks the deployment live when its HEAD
is that publish commit. Runs and publishes are mutually exclusive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 17:34 Failure
… publishes failed

Set the cancelled phase before killing the login/deploy child so their exit
handlers no-op instead of overwriting the deployment row with 'failed'.
Found by the headless publish-flow validation against the beta stack.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 17, 2026 17:58 Failure
…never parsed

CONSOLE_SCRIPT is authored in a TS template literal, so \n escapes in
client-facing strings (revert alert, whoami box, publish log tail) were
processed server-side into real newlines inside JS string literals. The
served script failed to parse (SyntaxError: Invalid or unexpected token),
loadData() never ran, and every admin panel hung at 'Loading…' while the
endpoints themselves were healthy.

- Escape the three occurrences as \\n so the browser receives \n.
- Compile CONSOLE_SCRIPT with new Function() at module load, without
  running it, so a future unparseable console script fails the deploy
  instead of shipping a dead console.
- Surface loadData() failures in the panels instead of silently leaving
  'Loading…' forever.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… in admin

Publish/deployment logs were already persisted to the deployments table, but
the admin console only showed the in-memory logTail while a publish was in the
deploying phase — the moment it failed, the panel dropped to just the error
banner and the streamed logs vanished, with no way to see why it failed.

- Add GET /admin/deployments/:id returning the full persisted log.
- Keep the publish list lightweight (drop log bodies from listDeployments,
  expose length only) so the 2s panel poll stays cheap.
- Make every deployment row clickable to open its stored log, and
  auto-open a failed publish's log so the error is visible in place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 14:18 Failure
… publish

The publish flow authenticates to GCP keyless via the VM's metadata-server
service account, whose access token carries no email — the stable CLI cannot
resolve the caller principal there ('no email found in token info') and every
publish died at account-info. The fix (DefangLabs/defang#2174, metadata-server
email fallback) is only in the nightly so far.

Resolve the current nightly asset at build time instead of pinning: nightly
assets are replaced daily, so a pinned asset URL would 404 on the next
self-rebuild and brick publishing. TODO: re-pin to the first stable release
containing #2174.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 18, 2026 16:08 Failure
…fig listing

The first real publish with working GCP identity got past account-info and
died at 'failed to list secrets: secretmanager.secrets.list denied' —
compose up validates the stack's config (POSTGRES_PASSWORD etc.) by listing
its Secret Manager secrets, and the x-defang-roles set predates that path
(every earlier deploy ran as an owner-level key, so it was never exercised).
Metadata-only viewer is enough: the CLI lists names; the CD (which has
secretmanager.admin) reads the values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 20, 2026 13:22 Failure
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 20, 2026 13:26 Failure
@defang-sam
defang-sam Bot temporarily deployed to deploy-changed-samples July 20, 2026 13:31 Inactive
@defang-sam
defang-sam Bot temporarily deployed to deploy-changed-samples July 20, 2026 14:20 Inactive
@defang-sam

defang-sam Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Consolidated the cloud configuration in 92ef6c2: AWS and GCP now share the single compose.yaml, compose.dev.yaml remains local-only, and the two stack files select .env.aws/.env.gcp. The provider-specific permission is supplied through the cross-cloud x-defang-policies field, and the private host port is shared (private on AWS; GCE-pinning on GCP). Default/AWS/GCP loader renders, exact two-file layout, agent typecheck, diff check, and an independent elegance review all pass. The PR description is updated; requesting another look.

Comment thread samples/self-updating-mastra/.defang/aws Outdated
Comment thread samples/self-updating-mastra/.defang/gcp Outdated
@lionello
lionello temporarily deployed to deploy-changed-samples July 21, 2026 07:41 — with GitHub Actions Inactive
@lionello
lionello temporarily deployed to deploy-changed-samples July 21, 2026 07:42 — with GitHub Actions Inactive
@lionello
lionello temporarily deployed to deploy-changed-samples July 21, 2026 07:47 — with GitHub Actions Inactive
@raphaeltm
raphaeltm temporarily deployed to deploy-changed-samples July 21, 2026 08:37 — with GitHub Actions Inactive
Replace the deprecated `provider: type: model` service with a top-level
`models:` element plus a `models:` binding on `dev` (matching compose.dev.yaml).
Defang synthesizes the LiteLLM gateway from the model entry and injects
CHAT_URL / CHAT_MODEL / OPENAI_API_KEY into the binding service, so the agent
wiring is unchanged. Also drops the dead `OPENAI_API_KEY: defang` that was set
on the old gateway service.

The top-level `models:` element cannot express per-service gateway memory, so
the gateway now uses the CLI default on both clouds instead of the former
per-cloud 2 GiB (AWS) / 512 MiB (GCP). Removed the now-unused CHAT_MEMORY from
.env.aws / .env.gcp and the gateway-memory column from the README, and
refreshed README + agent/src/model.ts wording that described chat as a
"model-provider service".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@defang-sam
defang-sam Bot temporarily deployed to deploy-changed-samples July 21, 2026 09:25 Inactive
Rename the sample to "Self-Improving Mastra": directory, compose project
names, the 1-click deploy template URL, agent package name and git identity,
in-app titles and prose, and the root samples-table entry. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@defang-sam
defang-sam Bot had a problem deploying to deploy-changed-samples July 21, 2026 09:37 Failure
@defang-sam defang-sam Bot changed the title feat(samples): add self-updating Mastra demo for AWS and GCP feat(samples): add self-improving Mastra demo for AWS and GCP Jul 21, 2026
The deploy-changed-samples staging filter excluded the BYOC-only sample by its
old directory name; the rename left it deploying (and failing on missing
ADMIN_TOKEN). Update the awk filter to self-improving-mastra so it is skipped
again (it exceeds the retired Playground staging quota).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@defang-sam
defang-sam Bot temporarily deployed to deploy-changed-samples July 21, 2026 09:41 Inactive
@lionello
lionello merged commit 3ea9bfc into main Jul 21, 2026
8 checks passed
@lionello
lionello deleted the feature/self-updating-mastra branch July 21, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants