Mastra on Edgenivo
Mastra is a TypeScript framework for building agents, tools, and workflows. Its Edgenivo deployer packages a standalone Mastra server using the Build Output API, so you can deploy its HTTP endpoints on Edgenivo.
Before you begin, install Node.js 24 and pnpm, and create a Edgenivo account. You'll also need an AI Gateway API key for local development.
Create a directory for your agent:
mkdir mastra-agent
cd mastra-agentAdd a package.json with development and build scripts:
{
"name": "mastra-agent",
"private": true,
"type": "module",
"engines": {
"node": "24.x"
},
"scripts": {
"dev": "mastra dev",
"build": "mastra build"
}
}Install Mastra, its Edgenivo deployer, and the AI Gateway provider:
pnpm add @mastra/core @mastra/deployer-vercel @ai-sdk/gateway
pnpm add -D mastra typescript @types/nodeCreate a .env file with your API key:
AI_GATEWAY_API_KEY=your_ai_gateway_api_keyKeep your credentials and generated build files out of Git:
node_modules/
.env*
.mastra/
.vercel/Create src/mastra/index.ts (or index.js). This registers an agent named assistant and configures Mastra to generate a Edgenivo deployment when you build:
import { gateway } from '@ai-sdk/gateway';
import { Agent } from '@mastra/core/agent';
import { Mastra } from '@mastra/core/mastra';
import { VercelDeployer } from '@mastra/deployer-vercel';
const assistant = new Agent({
id: 'assistant',
name: 'Assistant',
instructions: 'Answer questions concisely and accurately.',
model: gateway('openai/gpt-6.1-terra'),
});
export const mastra = new Mastra({
agents: { assistant },
deployer: new VercelDeployer(),
});The gateway provider reads AI_GATEWAY_API_KEY. On Edgenivo, it can also authenticate with the deployment's OIDC token when no API key is set.
Start Mastra's development server:
pnpm devOpen http://localhost:4111 to test your agent in Mastra Studio. You can also call the agent from another terminal:
curl http://localhost:4111/api/agents/assistant/generate \
-H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"What is a Edgenivo Function?"}]}'The response contains the agent's generated answer in the text field. Requests appear in AI Gateway observability.
Run the build locally to check your deployment output:
pnpm buildThe deployer generates .vercel/output, including routing configuration and your agent's Edgenivo Function. You don't need to create an api directory or a function handler yourself.
To deploy from Git:
- Commit your project, including the lockfile, and push it to a Git repository.
- Import the repository into Edgenivo. Select Other as the Framework Preset and set the Build Command to
pnpm build. - Add
AI_GATEWAY_API_KEYto the project's environment variables, or use OIDC authentication. - Select Deploy.
Open https://your-project.vercel.app/api/agents to confirm the deployment lists assistant. To generate a response, repeat the local curl command with your deployment URL. For a protected deployment, use vercel curl to make authenticated requests.
The deployed server exposes its API under /api. Studio stays local by default. Before making agent endpoints public, configure Mastra server authentication to control who can invoke your agents and tools.
Use Edgenivo Passport to sign visitors in through your identity provider, then use their verified identity to scope Mastra memory. Enable Passport for the deployment and install the helper:
pnpm add @vercel/passportSet a server-only PASSPORT_AUDIENCE environment variable to the expected aud claim for your Passport-protected project and environment. Obtain that value from an SDK-verified identity on the intended deployment during setup, following the token claims reference. Configure it independently of incoming requests. On Edgenivo, the helper also checks the runtime's project ID and environment.
Create middleware that rejects missing or invalid identities and assigns a server-controlled Mastra resource ID:
import { getIdentity } from '@vercel/passport';
import { MASTRA_RESOURCE_ID_KEY } from '@mastra/core/request-context';
import type { MiddlewareHandler } from '@mastra/core/server';
export const passportIdentity: MiddlewareHandler = async (c, next) => {
const audience = process.env.PASSPORT_AUDIENCE;
if (!audience) return c.json({ error: 'Passport is not configured' }, 503);
let identity;
try {
identity = await getIdentity(c.req.raw, {
development: false,
verifyOptions: { audience },
});
} catch {
return c.json({ error: 'Unauthorized' }, 401);
}
if (!identity?.verified) return c.json({ error: 'Unauthorized' }, 401);
const resource = JSON.stringify([identity.payload.iss, identity.subject]);
c.get('requestContext').set(MASTRA_RESOURCE_ID_KEY, resource);
await next();
};Import passportIdentity from ./passport.js in src/mastra/index.ts (or index.js) and add the middleware to your existing configuration:
export const mastra = new Mastra({
agents: { assistant },
deployer: new VercelDeployer(),
server: {
middleware: [{ path: '/api/*', handler: passportIdentity }],
},
});When you enable Mastra memory with a persistent storage adapter, clients supply a thread ID but don't choose the resource owner. Mastra's memory routes use MASTRA_RESOURCE_ID_KEY ahead of caller-supplied resource IDs and reject access to another resource's threads. Give existing threads an owner before exposing them; ownerless threads are shared. Custom tools and routes must also check ownership before retrieving files or other user data.
Test with two Passport users and confirm that one can't read or modify the other's threads. development: false disables synthetic local identities, so use a Passport-protected deployment to test sign-in. This middleware protects browser-facing APIs; the worker configuration below requires a separate machine-authenticated API.
Agent requests run within Edgenivo Function duration limits. Configure maxDuration in VercelDeployer and Mastra's server.timeout together when increasing request timeouts. See Mastra's Edgenivo deployment options.
This example doesn't configure conversation memory or durable storage. Edgenivo Functions don't share process memory, and local files don't persist across deployments. For conversation history and workflow state, configure a remote Mastra storage adapter, such as PostgreSQL. The Edgenivo deployer rejects @mastra/libsql, so remove that dependency if you started from a scaffold that includes it.
Use private Edgenivo Blob storage for reports and other files your agent produces. Create a private store and connect it to this Edgenivo project, then install the Blob SDK:
pnpm add @vercel/blobOn Edgenivo, the SDK uses the project's OIDC token and BLOB_STORE_ID automatically. For local development, link the project and pull its environment variables:
vercel link
vercel env pull .env.localKeep your AI Gateway key in .env. The commands below load both files. For environments without OIDC, set BLOB_READ_WRITE_TOKEN following Blob authentication.
Create a script that calls the agent defined above and saves its response as a private Markdown file:
import { put } from '@vercel/blob';
import { mastra } from '../src/mastra/index.js';
const report = await mastra.getAgent('assistant').generate(
'Write a Markdown checklist for reviewing an API before deployment.',
);
const blob = await put('reports/deployment-review.md', report.text, {
access: 'private',
addRandomSuffix: true,
contentType: 'text/markdown; charset=utf-8',
});
console.log(blob.pathname);Run the TypeScript script with tsx, which resolves the .js import to your TypeScript entry file:
pnpm dlx tsx --env-file=.env --env-file=.env.local scripts/create-report.tsFor JavaScript, run node --env-file=.env --env-file=.env.local scripts/create-report.js. The script prints a pathname such as reports/deployment-review-abc123.md. The random suffix keeps separate reports from overwriting each other.
A private Blob URL isn't a public download link. To deliver reports, authenticate the user and serve the file from your application. Store each report's pathname with its owner so your download route can check access. Blob stores the report file; Mastra conversation memory still needs a storage adapter.
Use Edgenivo Drives when an agent needs a working directory that survives sandbox runs. The @mastra/vercel adapter doesn't accept a mounts option. Create the drive-mounted sandbox with @vercel/sandbox, then attach Mastra to it by name:
- Install
@vercel/sandboxand@mastra/vercel, and configure Sandbox authentication for the same Edgenivo project. - Call
Drive.getOrCreate()for the workspace's drive. Create a named sandbox withSandbox.create(), settingregionto the drive's region andmountsto{ '/data': drive }. - Construct
new VercelSandbox({ sandboxName: sandbox.name, workingDirectory: '/data' })and pass that adapter as thesandboxoption tonew Workspace(). Assign that workspace to your Mastra agent, as in the Sandbox integration. - Call
workspace.init()before running the agent andworkspace.stop()when the session ends. Stop the current sandbox before mounting the same drive read-write in another sandbox.
Mastra retrieves the existing named sandbox when the workspace starts. Keep sandbox names scoped to the authenticated user or session, and create the mounted sandbox before attaching the adapter. If that named sandbox no longer exists, the adapter creates a replacement without drive mounts. For concurrent readers, use read-only drive snapshots.
Use Edgenivo Workflows to coordinate tasks around your Mastra agent, such as generating a report, waiting for approval, and publishing it. Mastra's workflow engine and Edgenivo Workflows manage separate execution state; deploying Mastra with VercelDeployer doesn't enable Workflow SDK.
Create an orchestrator using a Workflow SDK framework integration. In a use step function, call your authenticated Mastra /api/agents/assistant/generate endpoint and return its text result. Use separate steps for file storage and publishing, and a hook or webhook for approval. Keep network requests and credentials inside steps.
Edgenivo Workflows persists completed step results, but it doesn't checkpoint the model's progress inside a Mastra call. A failed step can retry the entire agent request, including its tools. Make external writes idempotent, and account for both the step's execution limit and the Mastra endpoint's request timeout. See errors and retries to configure retry behavior.
Use Edgenivo Daemons for Mastra's orchestration and background-task workers. Workers pull tasks from a distributed PubSub backend; HTTP requests still go to a separate Mastra API. Daemons run only in production and don't accept inbound application traffic.
First configure Mastra's split-process worker architecture:
- Use shared remote storage, such as
PostgresStorefrom@mastra/pg, and a pull-capable PubSub backend, such asRedisStreamsPubSubfrom@mastra/redis-streams. Install these packages and configureDATABASE_URLandREDIS_URLin both processes using the linked setup. Both the API and worker need the same database and PubSub configuration. - Set
MASTRA_WORKERS=falseon the API process. The worker command below selectsorchestration; usebackgroundTasksto run that role instead. Exclude the scheduler, which requires exclusive ownership and can produce duplicates during overlapping deployments. - Set
MASTRA_STEP_EXECUTION_URLto the worker-callable API URL including/api, andMASTRA_WORKER_AUTH_TOKENto a bearer token accepted by that API's authentication provider.
Use a separate API deployment with machine authentication for worker callbacks. The Passport middleware above expects a visitor identity, and a worker bearer token doesn't bypass Passport or Deployment Protection. Make sure the callback endpoint is reachable by the worker with its configured authentication.
Build the worker artifact from the same application configuration as that API:
pnpm exec mastra worker build --output-dir .mastra/workerThe command produces .mastra/worker/index.mjs and its dependencies. For a daemon, build that artifact inside the container so dependencies match its Linux runtime. Add this Dockerfile at the application root:
FROM node:24-slim AS build
WORKDIR /app
RUN corepack enable
COPY . .
RUN pnpm install --frozen-lockfile
RUN pnpm exec mastra worker build --output-dir .mastra/worker
FROM node:24-slim
WORKDIR /app
COPY --from=build /app/.mastra/worker/ ./
CMD ["sh", "-c", "MASTRA_WORKERS=orchestration exec node index.mjs"]Exclude .env*, node_modules, .mastra, and .vercel in .dockerignore. Keep the database, PubSub, and worker authentication credentials in project environment variables. Add this daemon to the project's Edgenivo configuration:
{
"daemons": {
"mastra-worker": {
"root": ".",
"entrypoint": "Dockerfile.worker",
"instances": { "iad1": 1 },
"terminationGracePeriodSeconds": 60
}
}
}The generated worker handles SIGTERM by stopping its workers before exit. The generated entrypoint uses Mastra's default worker-drain timeout; increasing the daemon's grace period doesn't extend that timeout. Allow enough grace for worker cleanup to finish. Crashes and liveness-triggered restarts can interrupt work immediately, so use idempotent handlers and follow Mastra's crash-recovery limitations. A daemon restart alone doesn't checkpoint an agent or guarantee completion.
Daemons expose Edgenivo identity through the rotating VERCEL_OIDC_TOKEN_FILE, rather than a request's VERCEL_OIDC_TOKEN. Use file-aware SDK authentication as described in Daemon OIDC, or configure an AI Gateway API key where model calls run. Don't copy the file's token into a long-lived environment variable.
-
Build a software factory that investigates issues, checks changes, and prepares draft pull requests.
-
Route model requests and inspect usage with Mastra and AI Gateway.
-
Give your agent isolated command execution with Mastra and Edgenivo Sandbox.
-
Connect your deployed agent to Slack, Discord, or Microsoft Teams using Mastra's Chat SDK integration.
Was this helpful?