Skip to content

Add a self-hosted Mastra Studio example - #1

Merged
andrelandgraf merged 7 commits into
mainfrom
self-hosted-studio
Aug 10, 2026
Merged

Add a self-hosted Mastra Studio example#1
andrelandgraf merged 7 commits into
mainfrom
self-hosted-studio

Conversation

@andrelandgraf

@andrelandgraf andrelandgraf commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Mastra Studio runs locally with Mastra's development server, but a hosted deployment also needs a static Studio build, an authenticated Mastra API, persistent memory, and observability storage. This repository had no runnable example showing how those pieces map onto Neon services.

Solution

This example runs the Mastra API and Studio asset proxy in Neon Functions, stores the SPA build in Object Storage, routes model calls through AI Gateway, and uses Lakebase Postgres for memory plus logs, traces, and metrics. Mastra SimpleAuth protects Studio, protected API routes, and the long-lived refresh stream with one deployment token.

Interface

Use a Neon project in aws-us-east-2 on the Launch or Scale plan:

bun install
cp .env.example .env.deploy
# Set MASTRA_STUDIO_TOKEN in .env.deploy.
set -a
source .env.deploy
set +a
neon link
neon checkout main
neon deploy --env .env.deploy
bun run studio:upload

Missing MASTRA_MODEL or MASTRA_STUDIO_TOKEN values stop the Neon CLI before deployment. Studio signs in with any email value and the configured token. It includes one memory-enabled assistant and three tools:

calculate
get-current-time
get-database-time

Local Studio runs on a fixed port:

neon dev
# http://localhost:8787

Mastra authenticates the routes it registers. Custom routes must call auth.getCurrentUser explicitly, as /refresh-events does.

Verification

  • bun install --frozen-lockfile
  • bun run typecheck
  • bun run test — 6 passed
  • Missing deployment settings were rejected during neon.ts evaluation.
  • Local and deployed Function health returned {"status":"ok"}; authenticated agent metadata reported gpt-5-4-mini and three tools.
  • Deployed Studio login, direct tool execution, streamed agent tool calls, memory persistence, logs, full trace trees, and automatic metrics passed.
  • Observability records survived local restart and Function redeployment.
  • Protected observability routes returned 401 without authentication.
  • /refresh-events returned 401 anonymously and opened an SSE stream with the bearer token or Studio session cookie.
  • Stored observability responses contained neither the Studio token nor the Postgres connection string.

Mastra Studio tool call

For attention

  • Functions, Object Storage, and AI Gateway are beta services not yet recommended for production workloads.
  • SimpleAuth is a shared-token example, not multi-user authentication.
  • Each open Studio tab holds one SSE Function invocation. Neon Functions default to 100 concurrent invocations per account.
  • Observability has its own schema but shares the application database. Each live Function isolate can open up to five Postgres connections.
  • Traces can contain prompts and model output. Production deployments need a data-specific redaction and retention policy.
  • The pinned Mastra observability packages are alpha releases.

Summary by CodeRabbit

  • New Features

    • Added a deployable Mastra Studio experience backed by Neon and object storage.
    • Added token-based Studio authentication and authenticated refresh-event streaming.
    • Added persistent-memory assistant capabilities with database-backed observability and tools.
    • Added arithmetic and current/database time tools.
    • Added health checks, asset serving, and production-ready API handling.
  • Documentation

    • Added setup, deployment, authentication, local development, and verification guidance.
  • Quality

    • Added automated type checking, tests, and CI validation for changes.

Run authenticated Studio, agent tools, memory, and observability entirely on Neon services.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The project adds a Bun-based Mastra Studio deployment for Neon. It configures PostgreSQL storage, authentication, tools, Studio asset upload and serving, health endpoints, lifecycle handling, CI checks, and deployment documentation.

Changes

Mastra Studio deployment

Layer / File(s) Summary
Project foundation
package.json, tsconfig.json, src/env.ts, .gitignore, .env.example, .github/workflows/ci.yml, AGENTS.md
The project adds Bun scripts and dependencies, strict TypeScript settings, required environment validation, repository exclusions, example variables, workflow instructions, and automated type-check and test jobs.
Mastra runtime and tools
src/mastra.ts, src/tools.ts
Mastra uses PostgreSQL storage, observability, resource-scoped memory, token authentication, a persistent-memory assistant, and arithmetic and timestamp tools.
Studio asset packaging and resolution
src/studio-assets.ts, scripts/upload-studio.ts, test/studio-assets.test.ts
Studio assets are configured, validated, uploaded to object storage, and served with cache and content-type metadata. Tests cover HTML configuration, path resolution, invalid paths, and encoded URLs.
HTTP serving and lifecycle
src/index.ts
The Hono application adds health and refresh-event endpoints, Studio asset serving, API fallbacks, readiness logging, observability flushing, and SIGINT shutdown handling.
Neon deployment wiring and documentation
neon.ts, README.md
Neon registers the Studio function and bucket, enables the AI gateway, validates deployment variables, and documents deployment, authentication, local development, and verification.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant HonoApp
  participant StudioAssetResolver
  participant ObjectStorage
  Browser->>HonoApp: request Studio asset
  HonoApp->>StudioAssetResolver: resolve pathname and Accept header
  StudioAssetResolver-->>HonoApp: asset key or index fallback
  HonoApp->>ObjectStorage: fetch asset object
  ObjectStorage-->>HonoApp: asset bytes and metadata
  HonoApp-->>Browser: serve asset response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a self-hosted Mastra Studio example.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch self-hosted-studio

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 16: Update the actions/checkout@v4 step in the CI workflow to set
persist-credentials to false, ensuring checkout does not retain GITHUB_TOKEN
credentials while preserving the existing checkout behavior.
- Line 22: Update the CI test step from the Bun-native runner command to the
declared package script by changing the run command to bun run test, so the
Vitest-based tests such as studio-assets.test.ts execute through the configured
test script.

In `@src/index.ts`:
- Around line 89-98: Update the Object Storage fetch in the upstream asset
request flow to use a finite timeout combined with context.req.raw.signal, using
Node.js 24-compatible APIs rather than Bun-specific tooling. Catch fetch
failures, return 504 when the timeout aborts the request, and return 502 for
other failures; preserve the existing upstream status handling for successful
responses.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 96d61108-acc7-4ec8-83a6-1baae3c0be1f

📥 Commits

Reviewing files that changed from the base of the PR and between 4b3df11 and 717f27d.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • docs/mastra-studio.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • .env.example
  • .github/workflows/ci.yml
  • .gitignore
  • README.md
  • neon.ts
  • package.json
  • scripts/upload-studio.ts
  • src/env.ts
  • src/index.ts
  • src/mastra.ts
  • src/studio-assets.ts
  • src/tools.ts
  • test/studio-assets.test.ts
  • tsconfig.json

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/ci.yml Outdated
Comment thread src/index.ts Outdated
Comment on lines +89 to +98
const upstream = await fetch(publicObjectUrl(storageEndpoint, key), {
method: context.req.method,
});

if (!upstream.ok) {
if (upstream.status === 404 && asset.type === 'asset') {
return context.text('Not found', 404);
}
return context.text('Studio assets unavailable', 502);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
git ls-files | sed -n '1,120p'

printf '%s\n' '--- runtime and dependency declarations ---'
rg -n --hidden -S \
  '("engines"|"runtime"|"bun"|"`@hono`|hono|AbortSignal|Neon|fetch\()' \
  package.json bunfig.toml wrangler.toml tsconfig.json src 2>/dev/null || true

printf '%s\n' '--- src/index.ts structure and relevant code ---'
if command -v ast-grep >/dev/null 2>&1; then
  ast-grep outline src/index.ts
fi
cat -n src/index.ts | sed -n '1,150p'

printf '%s\n' '--- request context and route usage ---'
rg -n -S \
  'context\.req|req\.raw|raw\.signal|publicObjectUrl|storageEndpoint|fetch\(' \
  src package.json README.md 2>/dev/null || true

Repository: neon-solutions/mastra-studio

Length of output: 6200


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- package and deployment metadata ---'
cat -n package.json
printf '%s\n' '--- README runtime/deployment references ---'
rg -n -C 3 -i 'bun|runtime|neon function|deploy|timeout|fetch|object storage|S3' README.md neon.ts .github/workflows/ci.yml bun.lock tsconfig.json
printf '%s\n' '--- lockfile runtime/package metadata ---'
head -80 bun.lock
printf '%s\n' '--- environment and deployment scripts ---'
cat -n neon.ts
cat -n scripts/upload-studio.ts | sed -n '1,220p'

Repository: neon-solutions/mastra-studio

Length of output: 50386


🌐 Web query:

Bun 1.3.14 AbortSignal.any AbortSignal.timeout support documentation

💡 Result:

In Bun v1.3.14, both AbortSignal.any and AbortSignal.timeout are supported as part of its standard Web API implementation [1][2][3][4]. AbortSignal.any This static method creates a new AbortSignal that aborts when any of the provided input signals are aborted [1][5]. It was introduced in Bun v1.1.4 [6]. Subsequent maintenance, including bug fixes for event propagation [7] and garbage collection reachability [8], has ensured its stability in later versions, including the current v1.3.14 release. AbortSignal.timeout This static method returns an AbortSignal that automatically aborts after a specified number of milliseconds [9]. While it is a supported feature in Bun [3][4], users may occasionally encounter TypeScript definition issues where the property is not recognized by the compiler [10]. As of Bun's recent updates, such issues are addressed, and ensuring that your project's TypeScript configuration includes the necessary DOM types (e.g., adding "DOM" to your lib array in tsconfig.json) typically resolves compilation errors [10]. Both methods follow the standard Web API specifications [11][9][5]. You can access their official documentation at the Bun website's globals reference section [1][3].

Citations:


🌐 Web query:

Neon Functions runtime Bun request abort signal client disconnect fetch timeout documentation

💡 Result:

Neon Functions currently run on the Node.js runtime (specifically Node.js 24) and do not support the Bun runtime [1][2][3]. While you may be interested in Bun's fetch capabilities—such as AbortSignal.timeout, per-request connectTimeout, and socketTimeout [4][5][6]—these features are specific to the Bun runtime environment and are not applicable to Neon Functions [1]. In the context of Neon Functions: 1. Runtime Environment: Neon Functions are designed to run JavaScript or TypeScript on Node.js [1]. The runtime platform enforces limits such as a 15-minute time-to-first-byte limit [7]. 2. Connection Management: Since Neon Functions run on Node.js, you should manage request timeouts and client disconnects using standard Node.js patterns (e.g., handling the abort event on an AbortSignal or monitoring connection state) rather than relying on Bun-specific APIs [1]. 3. Client Disconnects: Neon Functions are long-running and support streaming; however, if the underlying platform stops a function (e.g., due to inactivity), it sends a SIGINT signal, which your code should handle to close connections and clean up resources [7]. For projects requiring the specific fetch and abort features you referenced, you would need to use an environment that supports the Bun runtime [4][5]. Neon Functions remain strictly Node.js-based during their current beta phase [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- concise deployment metadata ---'
sed -n '1,220p' neon.ts
printf '%s\n' '--- concise package and CI runtime pins ---'
sed -n '1,45p' package.json
sed -n '1,40p' .github/workflows/ci.yml
printf '%s\n' '--- all abort and timeout references ---'
rg -n -S 'AbortSignal|timeout|signal|fetch\(' --glob '!bun.lock' .

Repository: neon-solutions/mastra-studio

Length of output: 1975


Bound the Object Storage request.

fetch() can remain pending while Object Storage stalls, and it does not use context.req.raw.signal. Add a finite timeout, combine it with the client abort signal, and return 504 for timeouts or 502 for other fetch failures. Neon Functions use Node.js 24, not the Bun version used for local tooling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.ts` around lines 89 - 98, Update the Object Storage fetch in the
upstream asset request flow to use a finite timeout combined with
context.req.raw.signal, using Node.js 24-compatible APIs rather than
Bun-specific tooling. Catch fetch failures, return 504 when the timeout aborts
the request, and return 502 for other failures; preserve the existing upstream
status handling for successful responses.

Prevent placeholder admin credentials and require authentication for long-lived refresh streams.
Reject incomplete environment configuration before provisioning an unusable Function.
Document the sign-in exception without overstating API protection.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@AGENTS.md`:
- Around line 3-4: Update the repository workflow instructions in AGENTS.md to
remove requirements to work directly on main or commit and push there; instruct
contributors to follow the standard branch and pull-request workflow, preserving
required verification before submission.

In `@README.md`:
- Line 8: Update the Lakebase Postgres on Neon link in the README to use
https://neon.com/docs/introduction/neon-and-lakebase instead of the generic Neon
documentation URL.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3aeb4555-406a-4eb7-8ab2-7e570888957e

📥 Commits

Reviewing files that changed from the base of the PR and between 409c86a and ab72e6e.

⛔ Files ignored due to path filters (1)
  • docs/mastra-studio.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • AGENTS.md
  • README.md
  • neon.ts
  • src/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/index.ts
  • neon.ts

Comment thread AGENTS.md
Comment thread README.md Outdated
@andrelandgraf
andrelandgraf merged commit 8b88aeb into main Aug 10, 2026
2 checks passed
@andrelandgraf
andrelandgraf deleted the self-hosted-studio branch August 10, 2026 15:03
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.

1 participant