Add a self-hosted Mastra Studio example - #1
Conversation
Run authenticated Studio, agent tools, memory, and observability entirely on Neon services.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe 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. ChangesMastra Studio deployment
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockdocs/mastra-studio.pngis excluded by!**/*.png
📒 Files selected for processing (14)
.env.example.github/workflows/ci.yml.gitignoreREADME.mdneon.tspackage.jsonscripts/upload-studio.tssrc/env.tssrc/index.tssrc/mastra.tssrc/studio-assets.tssrc/tools.tstest/studio-assets.test.tstsconfig.json
| 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); | ||
| } |
There was a problem hiding this comment.
🩺 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 || trueRepository: 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:
- 1: https://bun.com/reference/globals/AbortSignal/any
- 2: https://bun.sh/reference/globals/AbortSignal/any
- 3: https://bun.sh/reference/globals/AbortSignal/timeout
- 4: https://bun.com/reference/globals/AbortSignal/timeout
- 5: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static
- 6: https://bun.com/blog/bun-v1.1.4
- 7: AbortSignal.any is not working as expected oven-sh/bun#11726
- 8: Fix cross-thread WeakPtr destruction in AbortSignal.any()'s GC reachability callback oven-sh/bun#32785
- 9: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static
- 10:
AbortSignal.timeoutnot working with typescript oven-sh/bun#18529 - 11: Implement
AbortSignal.any()static method oven-sh/bun#10251
🌐 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:
- 1: https://neon.com/docs/compute/functions/overview
- 2: https://neon.com/docs/compute/functions/get-started
- 3: https://neon.com/docs/compute/functions/deploy.md
- 4: https://bun.com/docs/runtime/networking/fetch
- 5: fetch: add connectTimeout, socketTimeout, and a whole-request timeout oven-sh/bun#33338
- 6: oven-sh/bun@bef0c74
- 7: https://neon.com/docs/compute/functions/reference/runtime-limits.md
🏁 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.
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
docs/mastra-studio.pngis excluded by!**/*.png
📒 Files selected for processing (4)
AGENTS.mdREADME.mdneon.tssrc/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/index.ts
- neon.ts
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-2on the Launch or Scale plan:Missing
MASTRA_MODELorMASTRA_STUDIO_TOKENvalues 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:Local Studio runs on a fixed port:
neon dev # http://localhost:8787Mastra authenticates the routes it registers. Custom routes must call
auth.getCurrentUserexplicitly, as/refresh-eventsdoes.Verification
bun install --frozen-lockfilebun run typecheckbun run test— 6 passedneon.tsevaluation.{"status":"ok"}; authenticated agent metadata reportedgpt-5-4-miniand three tools.401without authentication./refresh-eventsreturned401anonymously and opened an SSE stream with the bearer token or Studio session cookie.For attention
Summary by CodeRabbit
New Features
Documentation
Quality