Skip to content

Commit b7dff0a

Browse files
authored
feat(core): add setStudio method for runtime studio configuration (#18111)
## Summary Adds `setStudio()` method to Mastra class, mirroring the existing `setServer()` method. This enables runtime configuration of studio auth/RBAC separately from server config. ## Why This Is Needed Platform deploy wrappers need to implement **dual auth**: - **Studio requests** (via `*.studio.mastra.cloud`): Use platform auth (`MastraAuthStudio`) - **Server requests** (via `*.server.mastra.cloud`): Open API access (no auth or user-configured auth) The dual auth flow: 1. Edge router sets `x-mastra-client-type: studio` header based on domain 2. `@mastra/server`'s `getEffectiveAuthConfig()` routes to `studio.auth` for studio requests 3. **Wrapper calls `setStudio()` to inject platform auth for studio requests** Without `setStudio()`, wrappers can only modify `server.auth` which applies to **all** requests, breaking the dual auth contract. ## Changes - Added `setStudio(studio: StudioConfig): void` method to `Mastra` class - Follows same pattern as `setServer()` ## Usage ```typescript // In platform deploy wrapper mastra.setStudio({ auth: new MastraAuthStudio(), rbac: new MastraRBACStudio({ roleMapping: { admin: ['*'] } }), }); ``` ## Related - Complements PR #17722 (dual auth system documentation) - Enables unified runtime deploys in mastra-ai/platform <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## ELI5 This PR lets Mastra configure “Studio” login/security separately from your API server’s login/security. So the admin dashboard (Studio) can use one auth/RBAC setup while the server API uses another—without mixing them up. ## Summary This PR adds a new `setStudio(studio: StudioConfig): void` method to the `Mastra` class (`packages/core/src/mastra/index.ts`) to configure Studio authentication and RBAC at runtime, independently from server configuration. ### What changed - **New API:** `public setStudio(studio: StudioConfig): void` - The method stores the provided Studio configuration on the instance’s private `#studio` field, mirroring the existing `setServer()` pattern. - **Dual-auth support (opt-in):** - **Studio requests** (`*.studio.mastra.cloud`) can use Studio-specific auth (e.g., `MastraAuthStudio`) and Studio-specific RBAC. - **Server requests** (`*.server.mastra.cloud`) can continue using server auth (or open API access) without being affected by Studio settings. - An edge router sets `x-mastra-client-type: studio` so the server-side routing logic can apply `studio.auth` for Studio-marked requests. ### Tests & documentation - Added a `setStudio` test suite verifying: - a new `Mastra` instance starts with `getStudio()` unset, - `setStudio()` sets/replaces the Studio config, - updates can be “merged” by passing an object derived from `...mastra.getStudio()` while changing nested fields (e.g., updating `auth` while preserving `rbac`). - Added a changeset documenting the new `setStudio()` feature and providing an example usage. ### Example ```ts mastra.setStudio({ auth: new MastraAuthStudio(), rbac: new MastraRBACStudio({ roleMapping: { admin: ['*'] } }), }); ``` <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent b91d921 commit b7dff0a

3 files changed

Lines changed: 73 additions & 0 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@mastra/core": minor
3+
---
4+
5+
Add `setStudio()` method to Mastra class for runtime studio configuration.
6+
7+
This enables deploy wrappers to configure studio auth/RBAC separately from server config, which is required for dual auth patterns where Studio UI uses platform auth while Server API remains open or uses user-configured auth.
8+
9+
```typescript
10+
// Set studio auth separately from server auth
11+
mastra.setStudio({
12+
auth: new MastraAuthStudio(),
13+
rbac: new MastraRBACStudio({ roleMapping: { admin: ['*'] } }),
14+
});
15+
```

packages/core/src/mastra/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,28 @@ export class Mastra<
964964
this.#server = server;
965965
}
966966

967+
/**
968+
* Sets the studio configuration for this Mastra instance.
969+
*
970+
* The studio configuration controls authentication and authorization for Studio UI,
971+
* separate from the server configuration. This enables dual auth patterns where
972+
* Studio users (e.g., internal team) use different auth than API consumers.
973+
*
974+
* @param studio - The studio configuration object
975+
*
976+
* @example
977+
* ```typescript
978+
* // Set studio auth separately from server auth
979+
* mastra.setStudio({
980+
* auth: new MastraAuthStudio(),
981+
* rbac: new MastraRBACStudio({ roleMapping: { admin: ['*'] } }),
982+
* });
983+
* ```
984+
*/
985+
public setStudio(studio: StudioConfig): void {
986+
this.#studio = studio;
987+
}
988+
967989
/**
968990
* Registers an exporter on the default observability instance.
969991
*
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { Mastra } from './index';
3+
4+
describe('Mastra.setStudio()', () => {
5+
it('should set studio config on a Mastra instance with no initial studio', () => {
6+
const mastra = new Mastra({ logger: false });
7+
8+
expect(mastra.getStudio()).toBeUndefined();
9+
10+
const studioConfig = { auth: { name: 'test-auth' } };
11+
mastra.setStudio(studioConfig as any);
12+
13+
expect(mastra.getStudio()).toEqual(studioConfig);
14+
});
15+
16+
it('should replace existing studio config', () => {
17+
const initialConfig = { auth: { name: 'initial-auth' } };
18+
const mastra = new Mastra({ logger: false, studio: initialConfig as any });
19+
20+
expect(mastra.getStudio()).toEqual(initialConfig);
21+
22+
const updatedConfig = { auth: { name: 'updated-auth' } };
23+
mastra.setStudio(updatedConfig as any);
24+
25+
expect(mastra.getStudio()).toEqual(updatedConfig);
26+
});
27+
28+
it('should allow merging with existing studio config via spread', () => {
29+
const initialConfig = { auth: { name: 'test-auth' }, rbac: { roleMapping: {} } };
30+
const mastra = new Mastra({ logger: false, studio: initialConfig as any });
31+
32+
mastra.setStudio({ ...mastra.getStudio(), auth: { name: 'new-auth' } } as any);
33+
34+
expect(mastra.getStudio()).toEqual({ auth: { name: 'new-auth' }, rbac: { roleMapping: {} } });
35+
});
36+
});

0 commit comments

Comments
 (0)