UNPKG

@mastra/core

Version:
1,503 lines 57.7 kB
const require_rolldown_runtime = require("./rolldown-runtime-uwYp4b74.cjs"); let crypto = require("crypto"); let os = require("os"); os = require_rolldown_runtime.__toESM(os, 1); //#region ../_internals/auth/dist/capabilities-ZXD8uYsD.js function hashTelemetryValue(value) { return (0, crypto.createHash)("sha256").update(value).digest("hex"); } function getHashedHostname() { return hashTelemetryValue(os.default.hostname() || "unknown-host").slice(0, 16); } function getEETelemetryFallbackDistinctId() { return `mastra-${getHashedHostname()}`; } const EE_TELEMETRY_BRIDGE = Symbol.for("mastra.eeTelemetryBridge"); function getTelemetryBridge() { return globalThis[EE_TELEMETRY_BRIDGE]; } function captureEEEvent(event, distinctId, properties) { getTelemetryBridge()?.captureEEEvent?.(event, distinctId, properties); } /** * License validation for EE features. * * Validation is delegated to the Mastra license server via `LicenseClient` * (POST {MASTRA_LICENSE_URL}/validate). The client validates in the * background and caches the result; the synchronous helpers in this module * read the cached state: * * - No license key configured → EE features disabled. * - Key configured, validation pending → fail open (features enabled) until * the first server response settles the state. * - Server says invalid/revoked/expired → EE features disabled. * - Server unreachable → fail open with a 72h grace period for previously * validated licenses. * * `MASTRA_LICENSE_KEY` is the primary env var; `MASTRA_EE_LICENSE` is a * supported legacy alias. */ var LicenseClient = class LicenseClient { static instance; logger; licenseKey; licenseUrl; mode = "open-source"; status = "pending"; cachedResult = null; cacheExpiry = 0; gracePeriodEnd = 0; revalidationTimeout = null; GRACE_PERIOD_MS = 4320 * 60 * 1e3; DEFAULT_TTL_MS = 1440 * 60 * 1e3; constructor(logger) { this.logger = logger; this.licenseKey = process.env.MASTRA_LICENSE_KEY || process.env.MASTRA_EE_LICENSE; this.licenseUrl = process.env.MASTRA_LICENSE_URL || "https://license.mastra.ai"; if (this.licenseKey) this.mode = "enterprise"; else this.mode = "open-source"; } static getInstance(logger) { if (!LicenseClient.instance) LicenseClient.instance = new LicenseClient(logger); else if (logger) LicenseClient.instance.logger = logger; return LicenseClient.instance; } /** * Reset the singleton so the next getInstance() re-reads env vars. * Intended for tests. */ static resetInstance() { if (LicenseClient.instance?.revalidationTimeout) clearTimeout(LicenseClient.instance.revalidationTimeout); LicenseClient.instance = void 0; } REQUEST_TIMEOUT_MS = 1e4; async fetchWithRetry(url, options, retries = 3) { for (let i = 0; i < retries; i++) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), this.REQUEST_TIMEOUT_MS); timer.unref?.(); try { const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal; const response = await fetch(url, { ...options, signal }); if (response.status === 429 || response.status >= 500) { if (i === retries - 1) return response; } else return response; } catch (error) { if (i === retries - 1) throw error; } finally { clearTimeout(timer); } const delay = Math.pow(2, i) * 1e3; await new Promise((resolve) => setTimeout(resolve, delay)); } throw new Error("Unreachable"); } validationPromise = null; async validate() { if (this.mode === "open-source") return true; if (this.cachedResult && Date.now() < this.cacheExpiry) return true; return this.revalidate(); } /** * Contact the server regardless of cache freshness, coalescing concurrent * callers (e.g. the Mastra constructor and the auth/ee helpers both kicking * off validation at startup) into a single in-flight request so the server * is contacted — and the outcome logged — only once. Used directly by the * background revalidation timer, which must bypass the cache check. */ revalidate() { if (!this.validationPromise) this.validationPromise = this.performValidation().finally(() => { this.validationPromise = null; }); return this.validationPromise; } async performValidation() { const now = Date.now(); try { if (!this.licenseUrl?.startsWith("https://") && !this.licenseUrl?.includes("localhost")) this.logger?.warn("License URL is not HTTPS. Proceeding, but this is insecure."); const response = await this.fetchWithRetry(`${this.licenseUrl}/validate`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ licenseKey: this.licenseKey }) }); if (response.status === 429 || response.status >= 500) throw new Error(`License server responded with ${response.status}`); const data = await response.json(); if (data.valid) { this.status = "valid"; this.logger?.info(`License validated: ${data.planTier} tier${data.expiresAt ? `, expires ${data.expiresAt.slice(0, 10)}` : ""}`); this.cachedResult = data; const ttlSeconds = data.leaseTtlSeconds || this.DEFAULT_TTL_MS / 1e3; this.cacheExpiry = now + ttlSeconds * 1e3; this.gracePeriodEnd = now + this.GRACE_PERIOD_MS; this.scheduleRevalidation(ttlSeconds); return true; } else if (data.code === "RATE_LIMITED") throw new Error(`License server rate limited: ${data.reason}`); else { this.status = "invalid"; this.logger?.error(`License validation failed: ${data.code} - ${data.reason}`); this.clearCache(); return false; } } catch { if (this.cachedResult && now < this.gracePeriodEnd) { this.logger?.warn("License server unreachable. Using cached license (within grace period)."); this.status = "valid"; this.scheduleRevalidation(this.DEFAULT_TTL_MS / 1e3); return true; } else if (this.cachedResult) { this.logger?.error("License server unreachable and grace period expired. Disabling enterprise features."); this.status = "invalid"; this.clearCache(); return false; } else { this.logger?.warn("License server unreachable on startup. Failing open (allowing features) and will retry."); this.status = "valid"; this.cachedResult = { valid: true, entitlements: [], planTier: "unknown", expiresAt: null, leaseTtlSeconds: 300 }; this.cacheExpiry = now + 300 * 1e3; this.gracePeriodEnd = now + this.GRACE_PERIOD_MS; this.scheduleRevalidation(300); return true; } } } scheduleRevalidation(ttlSeconds) { if (this.revalidationTimeout) clearTimeout(this.revalidationTimeout); const revalidateMs = ttlSeconds * 1e3 * .75; this.revalidationTimeout = setTimeout(() => { this.logger?.info("Performing background license revalidation..."); this.revalidate().catch((err) => { this.logger?.error(`Background license revalidation failed: ${err instanceof Error ? err.message : String(err)}`); }); }, revalidateMs); this.revalidationTimeout.unref(); } clearCache() { this.cachedResult = null; this.cacheExpiry = 0; this.gracePeriodEnd = 0; this.status = "invalid"; if (this.revalidationTimeout) { clearTimeout(this.revalidationTimeout); this.revalidationTimeout = null; } } hasFeature(featureName) { if (this.mode === "open-source") return true; if (this.status === "pending") return true; if (this.status === "invalid") return false; if (!this.cachedResult) return false; if (this.cachedResult.planTier === "unknown") return true; return this.cachedResult.entitlements.includes(featureName); } getEntitlements() { if (this.mode === "open-source") return null; return this.cachedResult?.entitlements || null; } getSnapshot() { return { mode: this.mode, status: this.status, entitlements: this.cachedResult?.entitlements ?? null, planTier: this.cachedResult?.planTier ?? null, expiresAt: this.cachedResult?.expiresAt ?? null }; } }; /** * Resolve the configured license key. * `MASTRA_LICENSE_KEY` is primary; `MASTRA_EE_LICENSE` is a supported legacy alias. */ function getLicenseKey() { return process.env["MASTRA_LICENSE_KEY"] || process.env["MASTRA_EE_LICENSE"]; } let validationStarted = false; let hasWarnedAboutDevLicense = false; /** * Get the shared LicenseClient and kick off background validation on first use. */ function getClient() { const client = LicenseClient.getInstance(); if (!validationStarted) { validationStarted = true; client.validate().catch(() => {}); } return client; } /** * Start license validation against the license server. * * Safe to call multiple times — the underlying client caches results and * schedules its own background revalidation. Resolves to whether the license * is currently considered valid. */ function startLicenseValidation() { const client = LicenseClient.getInstance(); validationStarted = true; return client.validate(); } /** * Validate the configured license and return license information. * * Reflects the current server-backed validation state. The actual network * validation happens in the background via `LicenseClient`, and only the * configured key (env var) is ever validated — passing any other key * returns invalid. * * @param licenseKey - Optional key to check; must match the configured key. * @returns License information */ function validateLicense(licenseKey) { const configuredKey = getLicenseKey(); if (!(licenseKey ?? configuredKey)) return { valid: false }; if (licenseKey !== void 0 && licenseKey !== configuredKey) return { valid: false }; const snap = getClient().getSnapshot(); return { valid: snap.status !== "invalid", features: snap.entitlements ?? void 0, tier: snap.planTier ?? void 0, expiresAt: snap.expiresAt ? new Date(snap.expiresAt) : void 0 }; } /** * Check if EE features are enabled (valid or pending server validation). * * @returns True if EE features should be enabled */ function isLicenseValid() { if (!getLicenseKey()) return false; return getClient().getSnapshot().status !== "invalid"; } /** * @deprecated Use `isLicenseValid()` instead. This alias is provided for backward compatibility. */ const isEELicenseValid = isLicenseValid; /** * Check if a specific EE feature is enabled by the license entitlements. * * @param feature - Feature name to check (e.g. 'rbac', 'fga', 'sso') * @returns True if the feature is enabled */ function isFeatureEnabled(feature) { if (!getLicenseKey()) return false; return getClient().hasFeature(feature); } function getSafeLicenseSummary() { const key = getLicenseKey(); const info = validateLicense(key); const licenseHash = key ? hashTelemetryValue(key) : void 0; return { valid: info.valid, isDevEnvironment: isDevEnvironment(), licenseHash: licenseHash ? licenseHash.slice(0, 16) : void 0, anonymousId: licenseHash ? `${licenseHash.slice(0, 16)}-anonymous` : void 0, features: info.features, tier: info.tier }; } function warnIfDevEENeedsLicense() { if (hasWarnedAboutDevLicense || !isDevEnvironment() || isLicenseValid()) return; hasWarnedAboutDevLicense = true; console.warn("[mastra/auth-ee] Mastra Enterprise features are enabled for local development, but no valid MASTRA_LICENSE_KEY is configured. These features will be disabled in production without a valid license. Contact us to get a production license: https://mastra.ai/contact"); } /** * Clear the license cache (useful for testing). * Resets the shared client so the next check re-reads env vars. */ function clearLicenseCache() { validationStarted = false; hasWarnedAboutDevLicense = false; LicenseClient.resetInstance(); } /** * Check if running in a development/testing environment. * In dev, EE features work without a license per the ee/LICENSE terms. */ function isDevEnvironment() { return process.env["MASTRA_DEV"] === "true" || process.env["MASTRA_DEV"] === "1" || process.env["NODE_ENV"] !== "production" && process.env["NODE_ENV"] !== "prod"; } /** * Check if EE features should be active. * Returns true if running in dev/test environment (always allowed) or if a valid license is present. */ function isEEEnabled() { if (isDevEnvironment()) { warnIfDevEENeedsLicense(); return true; } return isLicenseValid(); } /** * Type guard to check if response is authenticated. */ function isAuthenticated(caps) { return "user" in caps && caps.user !== null; } /** * Check if an auth provider implements a specific interface. */ function implementsInterface(auth, method) { return auth !== null && typeof auth === "object" && typeof auth[method] === "function"; } /** * Check if auth provider is MastraCloudAuth (exempt from license requirement). */ function isMastraCloudAuth(auth) { if (!auth || typeof auth !== "object") return false; return "isMastraCloudAuth" in auth && auth.isMastraCloudAuth === true; } /** * Check if auth provider is SimpleAuth (exempt from license requirement). * SimpleAuth is for development/testing and should work without a license. */ function isSimpleAuth(auth) { if (!auth || typeof auth !== "object") return false; return "isSimpleAuth" in auth && auth.isSimpleAuth === true; } /** * Check if a set of permissions includes admin bypass (`*` or `*:*`). */ function hasAdminBypassPermissions(permissions) { return permissions.some((p) => p === "*" || p === "*:*"); } function getRequestIp(request) { const forwardedFor = request.headers.get("x-forwarded-for"); if (forwardedFor) return forwardedFor.split(",")[0]?.trim(); return request.headers.get("x-real-ip") ?? void 0; } function captureLicenseCheck({ request, user, hasLicense, isDev, isCloud, isSimple, capabilities }) { const license = getSafeLicenseSummary(); try { const ip = getRequestIp(request); captureEEEvent("ee_license_check", user?.id || license.anonymousId || getEETelemetryFallbackDistinctId(), { license_valid: hasLicense, license_hash: license.licenseHash, is_dev_environment: isDev, is_cloud: isCloud, is_simple_auth: isSimple, capabilities, user_id: user?.id, $ip: ip, license_features: license.features, license_tier: license.tier }); } catch {} } /** * Build capabilities response based on auth configuration and request state. * * This function determines what capabilities are available and, if the user * is authenticated, includes their user info and access permissions. * * @param auth - Auth provider (or null if no auth configured) * @param request - Incoming HTTP request * @param options - Optional configuration (roleMapping, etc.) * @returns Capabilities response (public or authenticated) */ async function buildCapabilities(auth, request, options) { if (!auth) return { enabled: false, login: null }; const hasLicense = isLicenseValid(); const isCloud = isMastraCloudAuth(auth); const isSimple = isSimpleAuth(auth); const isDev = isDevEnvironment(); if (isDev && !hasLicense) warnIfDevEENeedsLicense(); const isLicensedOrCloud = hasLicense || isCloud || isSimple || isDev; const isFeatureLicensed = (feature) => isCloud || isSimple || isDev || hasLicense && isFeatureEnabled(feature); let login = null; const hasSSO = implementsInterface(auth, "getLoginUrl") && isLicensedOrCloud; const hasCredentials = implementsInterface(auth, "signIn") && isLicensedOrCloud; const raw = (options?.apiPrefix || "/api").trim(); const withSlash = raw.startsWith("/") ? raw : `/${raw}`; const ssoLoginUrl = `${withSlash.endsWith("/") ? withSlash.slice(0, -1) : withSlash}/auth/sso/login`; let signUpEnabled = true; if (implementsInterface(auth, "signIn")) { const credentialsProvider = auth; if (typeof credentialsProvider.isSignUpEnabled === "function") signUpEnabled = credentialsProvider.isSignUpEnabled(); } if (hasSSO && hasCredentials) { const ssoConfig = auth.getLoginButtonConfig(); login = { type: "both", signUpEnabled, description: ssoConfig.description, sso: { ...ssoConfig, url: ssoLoginUrl } }; } else if (hasSSO) { const ssoConfig = auth.getLoginButtonConfig(); login = { type: "sso", description: ssoConfig.description, sso: { ...ssoConfig, url: ssoLoginUrl } }; } else if (hasCredentials) login = { type: "credentials", signUpEnabled }; let user = null; if (implementsInterface(auth, "getCurrentUser") && isLicensedOrCloud) try { user = await auth.getCurrentUser(request); } catch { user = null; } if (!user) { captureLicenseCheck({ request, user, hasLicense, isDev, isCloud, isSimple }); return { enabled: true, login }; } const rbacProvider = options?.rbac; const hasRBAC = !!rbacProvider && isFeatureLicensed("rbac"); const hasFGA = !!options?.fga && isFeatureLicensed("fga"); const capabilities = { user: implementsInterface(auth, "getCurrentUser") && isLicensedOrCloud, session: implementsInterface(auth, "createSession") && isLicensedOrCloud, sso: implementsInterface(auth, "getLoginUrl") && isLicensedOrCloud, rbac: hasRBAC, acl: implementsInterface(auth, "canAccess") && isFeatureLicensed("acl"), fga: hasFGA }; let access = null; if (hasRBAC && rbacProvider) try { const roles = await rbacProvider.getRoles(user); const permissions = await rbacProvider.getPermissions(user); access = { roles, permissions }; const license = getSafeLicenseSummary(); try { const ip = getRequestIp(request); captureEEEvent("ee_feature_used", user.id || license.anonymousId || getEETelemetryFallbackDistinctId(), { feature: "rbac", user_id: user.id, organization_membership_id: user.metadata?.["organizationMembershipId"], role_count: roles.length, permission_count: permissions.length, $ip: ip, license_valid: license.valid, license_hash: license.licenseHash, is_dev_environment: license.isDevEnvironment }); } catch {} } catch { access = null; } let availableRoles; if (access && rbacProvider?.getAvailableRoles) { if (hasAdminBypassPermissions(access.permissions)) try { const allRoles = await rbacProvider.getAvailableRoles(); const getPermissionsForRole = rbacProvider.getPermissionsForRole?.bind(rbacProvider); if (getPermissionsForRole) availableRoles = (await Promise.allSettled(allRoles.map(async (role) => ({ role, perms: await getPermissionsForRole(role.id) })))).flatMap((result) => { if (result.status !== "fulfilled") { console.warn("[auth/ee] failed to list permissions for role:", result.reason); return []; } return hasAdminBypassPermissions(result.value.perms) ? [] : [result.value.role]; }); else availableRoles = allRoles; } catch (error) { console.warn("[auth/ee] failed to list available roles for admin user:", error); } } captureLicenseCheck({ request, user, hasLicense, isDev, isCloud, isSimple, capabilities }); return { enabled: true, login, user: { id: user.id, email: user.email, name: user.name, avatarUrl: user.avatarUrl }, capabilities, access, availableRoles }; } //#endregion //#region ../_internals/auth/dist/ee-DXvSoTl7.js /** * AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY * * This file is generated by packages/server/scripts/generate-permissions.ts * Run `pnpm generate:permissions` from packages/server to regenerate. * * Source of truth: SERVER_ROUTES in @mastra/server */ /** * All known API resources. * Derived from SERVER_ROUTES paths in @mastra/server. */ const RESOURCES = [ "a2a", "agent-builder", "agent-controller", "agents", "auth", "background-tasks", "channels", "datasets", "embedders", "experiments", "infrastructure", "logs", "mcp", "memory", "observability", "processor-providers", "processors", "schedules", "scores", "stored-agents", "stored-mcp-clients", "stored-prompt-blocks", "stored-scorers", "stored-skills", "stored-workflows", "stored-workspaces", "system", "tool-providers", "tools", "vector", "vectors", "workflows", "workspaces" ]; /** * All permission actions. * Derived from HTTP methods and route overrides: * - GET → read * - POST → write or execute (context-dependent) * - PUT/PATCH → write * - DELETE → delete * - Additional actions from explicit requiresPermission overrides */ const ACTIONS = [ "create", "delete", "execute", "publish", "read", "share", "write" ]; /** * All valid permission patterns. * Use `keyof typeof PERMISSION_PATTERNS` or the `PermissionPattern` type. */ const PERMISSION_PATTERNS = { /** Full access to all resources and actions */ "*": "*", /** Create all resources */ "*:create": "*:create", /** Delete all resources */ "*:delete": "*:delete", /** Execute all resources */ "*:execute": "*:execute", /** Publish, activate, or restore all resources */ "*:publish": "*:publish", /** View all resources */ "*:read": "*:read", /** Change visibility/audience all resources */ "*:share": "*:share", /** Create and modify all resources */ "*:write": "*:write", /** Full access to agent-to-agent communication */ "a2a:*": "a2a:*", /** Full access to agent builder */ "agent-builder:*": "agent-builder:*", /** Full access to agent controller sessions */ "agent-controller:*": "agent-controller:*", /** Full access to agents */ "agents:*": "agents:*", /** Full access to auth */ "auth:*": "auth:*", /** Full access to background tasks */ "background-tasks:*": "background-tasks:*", /** Full access to channels */ "channels:*": "channels:*", /** Full access to datasets */ "datasets:*": "datasets:*", /** Full access to embedders */ "embedders:*": "embedders:*", /** Full access to experiments */ "experiments:*": "experiments:*", /** Full access to infrastructure */ "infrastructure:*": "infrastructure:*", /** Full access to logs */ "logs:*": "logs:*", /** Full access to MCP servers */ "mcp:*": "mcp:*", /** Full access to memory and threads */ "memory:*": "memory:*", /** Full access to traces and spans */ "observability:*": "observability:*", /** Full access to processor-providers */ "processor-providers:*": "processor-providers:*", /** Full access to processors */ "processors:*": "processors:*", /** Full access to schedules */ "schedules:*": "schedules:*", /** Full access to evaluation scores */ "scores:*": "scores:*", /** Full access to stored agents */ "stored-agents:*": "stored-agents:*", /** Full access to stored MCP clients */ "stored-mcp-clients:*": "stored-mcp-clients:*", /** Full access to stored prompt blocks */ "stored-prompt-blocks:*": "stored-prompt-blocks:*", /** Full access to stored scorers */ "stored-scorers:*": "stored-scorers:*", /** Full access to stored skills */ "stored-skills:*": "stored-skills:*", /** Full access to stored workflows */ "stored-workflows:*": "stored-workflows:*", /** Full access to stored workspaces */ "stored-workspaces:*": "stored-workspaces:*", /** Full access to system info */ "system:*": "system:*", /** Full access to tool-providers */ "tool-providers:*": "tool-providers:*", /** Full access to tools */ "tools:*": "tools:*", /** Full access to vector stores */ "vector:*": "vector:*", /** Full access to vectors */ "vectors:*": "vectors:*", /** Full access to workflows */ "workflows:*": "workflows:*", /** Full access to workspaces */ "workspaces:*": "workspaces:*", /** View agent-to-agent communication */ "a2a:read": "a2a:read", /** Create and modify agent-to-agent communication */ "a2a:write": "a2a:write", /** Execute agent builder */ "agent-builder:execute": "agent-builder:execute", /** View agent builder */ "agent-builder:read": "agent-builder:read", /** Create and modify agent builder */ "agent-builder:write": "agent-builder:write", /** Execute agent controller sessions */ "agent-controller:execute": "agent-controller:execute", /** View agent controller sessions */ "agent-controller:read": "agent-controller:read", /** Create agents */ "agents:create": "agents:create", /** Delete agents */ "agents:delete": "agents:delete", /** Execute agents */ "agents:execute": "agents:execute", /** View agents */ "agents:read": "agents:read", /** Create and modify agents */ "agents:write": "agents:write", /** View auth */ "auth:read": "auth:read", /** View background tasks */ "background-tasks:read": "background-tasks:read", /** View channels */ "channels:read": "channels:read", /** Create and modify channels */ "channels:write": "channels:write", /** Delete datasets */ "datasets:delete": "datasets:delete", /** Execute datasets */ "datasets:execute": "datasets:execute", /** View datasets */ "datasets:read": "datasets:read", /** Create and modify datasets */ "datasets:write": "datasets:write", /** View embedders */ "embedders:read": "embedders:read", /** View experiments */ "experiments:read": "experiments:read", /** View infrastructure */ "infrastructure:read": "infrastructure:read", /** View logs */ "logs:read": "logs:read", /** Execute MCP servers */ "mcp:execute": "mcp:execute", /** View MCP servers */ "mcp:read": "mcp:read", /** Create and modify MCP servers */ "mcp:write": "mcp:write", /** Delete memory and threads */ "memory:delete": "memory:delete", /** Execute memory and threads */ "memory:execute": "memory:execute", /** View memory and threads */ "memory:read": "memory:read", /** Create and modify memory and threads */ "memory:write": "memory:write", /** View traces and spans */ "observability:read": "observability:read", /** Create and modify traces and spans */ "observability:write": "observability:write", /** View processor-providers */ "processor-providers:read": "processor-providers:read", /** Execute processors */ "processors:execute": "processors:execute", /** View processors */ "processors:read": "processors:read", /** Delete schedules */ "schedules:delete": "schedules:delete", /** Execute schedules */ "schedules:execute": "schedules:execute", /** View schedules */ "schedules:read": "schedules:read", /** Create and modify schedules */ "schedules:write": "schedules:write", /** View evaluation scores */ "scores:read": "scores:read", /** Create and modify evaluation scores */ "scores:write": "scores:write", /** Delete stored agents */ "stored-agents:delete": "stored-agents:delete", /** Publish, activate, or restore stored agents */ "stored-agents:publish": "stored-agents:publish", /** View stored agents */ "stored-agents:read": "stored-agents:read", /** Create and modify stored agents */ "stored-agents:write": "stored-agents:write", /** Delete stored MCP clients */ "stored-mcp-clients:delete": "stored-mcp-clients:delete", /** Publish, activate, or restore stored MCP clients */ "stored-mcp-clients:publish": "stored-mcp-clients:publish", /** View stored MCP clients */ "stored-mcp-clients:read": "stored-mcp-clients:read", /** Create and modify stored MCP clients */ "stored-mcp-clients:write": "stored-mcp-clients:write", /** Delete stored prompt blocks */ "stored-prompt-blocks:delete": "stored-prompt-blocks:delete", /** Publish, activate, or restore stored prompt blocks */ "stored-prompt-blocks:publish": "stored-prompt-blocks:publish", /** View stored prompt blocks */ "stored-prompt-blocks:read": "stored-prompt-blocks:read", /** Create and modify stored prompt blocks */ "stored-prompt-blocks:write": "stored-prompt-blocks:write", /** Delete stored scorers */ "stored-scorers:delete": "stored-scorers:delete", /** Publish, activate, or restore stored scorers */ "stored-scorers:publish": "stored-scorers:publish", /** View stored scorers */ "stored-scorers:read": "stored-scorers:read", /** Create and modify stored scorers */ "stored-scorers:write": "stored-scorers:write", /** Delete stored skills */ "stored-skills:delete": "stored-skills:delete", /** Publish, activate, or restore stored skills */ "stored-skills:publish": "stored-skills:publish", /** View stored skills */ "stored-skills:read": "stored-skills:read", /** Create and modify stored skills */ "stored-skills:write": "stored-skills:write", /** View stored workflows */ "stored-workflows:read": "stored-workflows:read", /** Create and modify stored workflows */ "stored-workflows:write": "stored-workflows:write", /** Delete stored workspaces */ "stored-workspaces:delete": "stored-workspaces:delete", /** View stored workspaces */ "stored-workspaces:read": "stored-workspaces:read", /** Create and modify stored workspaces */ "stored-workspaces:write": "stored-workspaces:write", /** View system info */ "system:read": "system:read", /** Delete tool-providers */ "tool-providers:delete": "tool-providers:delete", /** View tool-providers */ "tool-providers:read": "tool-providers:read", /** Create and modify tool-providers */ "tool-providers:write": "tool-providers:write", /** Execute tools */ "tools:execute": "tools:execute", /** View tools */ "tools:read": "tools:read", /** Delete vector stores */ "vector:delete": "vector:delete", /** Execute vector stores */ "vector:execute": "vector:execute", /** View vector stores */ "vector:read": "vector:read", /** Create and modify vector stores */ "vector:write": "vector:write", /** View vectors */ "vectors:read": "vectors:read", /** Delete workflows */ "workflows:delete": "workflows:delete", /** Execute workflows */ "workflows:execute": "workflows:execute", /** View workflows */ "workflows:read": "workflows:read", /** Create and modify workflows */ "workflows:write": "workflows:write", /** Delete workspaces */ "workspaces:delete": "workspaces:delete", /** View workspaces */ "workspaces:read": "workspaces:read", /** Create and modify workspaces */ "workspaces:write": "workspaces:write", /** Full access to all stored resource families */ "stored:*": "stored:*", /** View all stored resource families */ "stored:read": "stored:read", /** Create and modify all stored resource families */ "stored:write": "stored:write", /** Delete all stored resource families */ "stored:delete": "stored:delete", /** Change visibility/audience stored agents */ "stored-agents:share": "stored-agents:share", /** Change visibility/audience stored skills */ "stored-skills:share": "stored-skills:share" }; /** * All valid resource:action permission combinations (excludes wildcards). */ const PERMISSIONS = [ "a2a:read", "a2a:write", "agent-builder:execute", "agent-builder:read", "agent-builder:write", "agent-controller:execute", "agent-controller:read", "agents:create", "agents:delete", "agents:execute", "agents:read", "agents:write", "auth:read", "background-tasks:read", "channels:read", "channels:write", "datasets:delete", "datasets:execute", "datasets:read", "datasets:write", "embedders:read", "experiments:read", "infrastructure:read", "logs:read", "mcp:execute", "mcp:read", "mcp:write", "memory:delete", "memory:execute", "memory:read", "memory:write", "observability:read", "observability:write", "processor-providers:read", "processors:execute", "processors:read", "schedules:delete", "schedules:execute", "schedules:read", "schedules:write", "scores:read", "scores:write", "stored-agents:delete", "stored-agents:publish", "stored-agents:read", "stored-agents:write", "stored-mcp-clients:delete", "stored-mcp-clients:publish", "stored-mcp-clients:read", "stored-mcp-clients:write", "stored-prompt-blocks:delete", "stored-prompt-blocks:publish", "stored-prompt-blocks:read", "stored-prompt-blocks:write", "stored-scorers:delete", "stored-scorers:publish", "stored-scorers:read", "stored-scorers:write", "stored-skills:delete", "stored-skills:publish", "stored-skills:read", "stored-skills:write", "stored-workflows:read", "stored-workflows:write", "stored-workspaces:delete", "stored-workspaces:read", "stored-workspaces:write", "system:read", "tool-providers:delete", "tool-providers:read", "tool-providers:write", "tools:execute", "tools:read", "vector:delete", "vector:execute", "vector:read", "vector:write", "vectors:read", "workflows:delete", "workflows:execute", "workflows:read", "workflows:write", "workspaces:delete", "workspaces:read", "workspaces:write" ]; /** * Type-safe constants for Mastra-owned FGA permissions. * * These values are generated from server routes and can be used wherever * Mastra checks or maps FGA permissions. */ const MastraFGAPermissions = { /** View agent-to-agent communication */ A2A_READ: "a2a:read", /** Create and modify agent-to-agent communication */ A2A_WRITE: "a2a:write", /** Execute agent builder */ AGENT_BUILDER_EXECUTE: "agent-builder:execute", /** View agent builder */ AGENT_BUILDER_READ: "agent-builder:read", /** Create and modify agent builder */ AGENT_BUILDER_WRITE: "agent-builder:write", /** Execute agent controller sessions */ AGENT_CONTROLLER_EXECUTE: "agent-controller:execute", /** View agent controller sessions */ AGENT_CONTROLLER_READ: "agent-controller:read", /** Create agents */ AGENTS_CREATE: "agents:create", /** Delete agents */ AGENTS_DELETE: "agents:delete", /** Execute agents */ AGENTS_EXECUTE: "agents:execute", /** View agents */ AGENTS_READ: "agents:read", /** Create and modify agents */ AGENTS_WRITE: "agents:write", /** View auth */ AUTH_READ: "auth:read", /** View background tasks */ BACKGROUND_TASKS_READ: "background-tasks:read", /** View channels */ CHANNELS_READ: "channels:read", /** Create and modify channels */ CHANNELS_WRITE: "channels:write", /** Delete datasets */ DATASETS_DELETE: "datasets:delete", /** Execute datasets */ DATASETS_EXECUTE: "datasets:execute", /** View datasets */ DATASETS_READ: "datasets:read", /** Create and modify datasets */ DATASETS_WRITE: "datasets:write", /** View embedders */ EMBEDDERS_READ: "embedders:read", /** View experiments */ EXPERIMENTS_READ: "experiments:read", /** View infrastructure */ INFRASTRUCTURE_READ: "infrastructure:read", /** View logs */ LOGS_READ: "logs:read", /** Execute MCP servers */ MCP_EXECUTE: "mcp:execute", /** View MCP servers */ MCP_READ: "mcp:read", /** Create and modify MCP servers */ MCP_WRITE: "mcp:write", /** Delete memory and threads */ MEMORY_DELETE: "memory:delete", /** Execute memory and threads */ MEMORY_EXECUTE: "memory:execute", /** View memory and threads */ MEMORY_READ: "memory:read", /** Create and modify memory and threads */ MEMORY_WRITE: "memory:write", /** View traces and spans */ OBSERVABILITY_READ: "observability:read", /** Create and modify traces and spans */ OBSERVABILITY_WRITE: "observability:write", /** View processor-providers */ PROCESSOR_PROVIDERS_READ: "processor-providers:read", /** Execute processors */ PROCESSORS_EXECUTE: "processors:execute", /** View processors */ PROCESSORS_READ: "processors:read", /** Delete schedules */ SCHEDULES_DELETE: "schedules:delete", /** Execute schedules */ SCHEDULES_EXECUTE: "schedules:execute", /** View schedules */ SCHEDULES_READ: "schedules:read", /** Create and modify schedules */ SCHEDULES_WRITE: "schedules:write", /** View evaluation scores */ SCORES_READ: "scores:read", /** Create and modify evaluation scores */ SCORES_WRITE: "scores:write", /** Delete stored agents */ STORED_AGENTS_DELETE: "stored-agents:delete", /** Publish, activate, or restore stored agents */ STORED_AGENTS_PUBLISH: "stored-agents:publish", /** View stored agents */ STORED_AGENTS_READ: "stored-agents:read", /** Create and modify stored agents */ STORED_AGENTS_WRITE: "stored-agents:write", /** Delete stored MCP clients */ STORED_MCP_CLIENTS_DELETE: "stored-mcp-clients:delete", /** Publish, activate, or restore stored MCP clients */ STORED_MCP_CLIENTS_PUBLISH: "stored-mcp-clients:publish", /** View stored MCP clients */ STORED_MCP_CLIENTS_READ: "stored-mcp-clients:read", /** Create and modify stored MCP clients */ STORED_MCP_CLIENTS_WRITE: "stored-mcp-clients:write", /** Delete stored prompt blocks */ STORED_PROMPT_BLOCKS_DELETE: "stored-prompt-blocks:delete", /** Publish, activate, or restore stored prompt blocks */ STORED_PROMPT_BLOCKS_PUBLISH: "stored-prompt-blocks:publish", /** View stored prompt blocks */ STORED_PROMPT_BLOCKS_READ: "stored-prompt-blocks:read", /** Create and modify stored prompt blocks */ STORED_PROMPT_BLOCKS_WRITE: "stored-prompt-blocks:write", /** Delete stored scorers */ STORED_SCORERS_DELETE: "stored-scorers:delete", /** Publish, activate, or restore stored scorers */ STORED_SCORERS_PUBLISH: "stored-scorers:publish", /** View stored scorers */ STORED_SCORERS_READ: "stored-scorers:read", /** Create and modify stored scorers */ STORED_SCORERS_WRITE: "stored-scorers:write", /** Delete stored skills */ STORED_SKILLS_DELETE: "stored-skills:delete", /** Publish, activate, or restore stored skills */ STORED_SKILLS_PUBLISH: "stored-skills:publish", /** View stored skills */ STORED_SKILLS_READ: "stored-skills:read", /** Create and modify stored skills */ STORED_SKILLS_WRITE: "stored-skills:write", /** View stored workflows */ STORED_WORKFLOWS_READ: "stored-workflows:read", /** Create and modify stored workflows */ STORED_WORKFLOWS_WRITE: "stored-workflows:write", /** Delete stored workspaces */ STORED_WORKSPACES_DELETE: "stored-workspaces:delete", /** View stored workspaces */ STORED_WORKSPACES_READ: "stored-workspaces:read", /** Create and modify stored workspaces */ STORED_WORKSPACES_WRITE: "stored-workspaces:write", /** View system info */ SYSTEM_READ: "system:read", /** Delete tool-providers */ TOOL_PROVIDERS_DELETE: "tool-providers:delete", /** View tool-providers */ TOOL_PROVIDERS_READ: "tool-providers:read", /** Create and modify tool-providers */ TOOL_PROVIDERS_WRITE: "tool-providers:write", /** Execute tools */ TOOLS_EXECUTE: "tools:execute", /** View tools */ TOOLS_READ: "tools:read", /** Delete vector stores */ VECTOR_DELETE: "vector:delete", /** Execute vector stores */ VECTOR_EXECUTE: "vector:execute", /** View vector stores */ VECTOR_READ: "vector:read", /** Create and modify vector stores */ VECTOR_WRITE: "vector:write", /** View vectors */ VECTORS_READ: "vectors:read", /** Delete workflows */ WORKFLOWS_DELETE: "workflows:delete", /** Execute workflows */ WORKFLOWS_EXECUTE: "workflows:execute", /** View workflows */ WORKFLOWS_READ: "workflows:read", /** Create and modify workflows */ WORKFLOWS_WRITE: "workflows:write", /** Delete workspaces */ WORKSPACES_DELETE: "workspaces:delete", /** View workspaces */ WORKSPACES_READ: "workspaces:read", /** Create and modify workspaces */ WORKSPACES_WRITE: "workspaces:write" }; /** * Validates that a string is a valid permission pattern. * Useful for runtime validation of permission strings. */ function isValidPermissionPattern(pattern) { return pattern in PERMISSION_PATTERNS; } /** * Validates that all permissions in an array are valid patterns. */ function validatePermissions(permissions) { return permissions.every(isValidPermissionPattern); } /** * FGA enforcement utility for checking fine-grained authorization. * * @license Mastra Enterprise License - see ee/LICENSE */ function mergeFGAContext({ context, requestContext, metadata }) { const mergedContext = { ...context }; if (requestContext) mergedContext.requestContext = requestContext; if (metadata || context?.metadata) mergedContext.metadata = { ...context?.metadata ?? {}, ...metadata ?? {} }; return Object.keys(mergedContext).length > 0 ? mergedContext : void 0; } function isActorSignal(actor) { if (actor === true) return true; if (typeof actor !== "object" || actor === null) return false; const candidate = actor; return candidate.actorKind === "system" && (candidate.sourceWorkflow === void 0 || typeof candidate.sourceWorkflow === "string"); } function getAgentFGAResourceId(agentId) { return agentId; } function getWorkflowFGAResourceId(workflowId) { return workflowId; } function getStandaloneToolFGAResourceId(toolName) { return toolName; } function getAgentToolFGAResourceId(agentId, toolName) { return `${agentId}:${toolName}`; } function getMCPToolFGAResourceId(serverName, toolName) { return JSON.stringify([serverName, toolName]); } /** * Check fine-grained authorization for a resource. * * No-op if no FGA provider is configured (backward compatibility). * Delegates to fgaProvider.require() which throws FGADeniedError if denied. */ async function checkFGA(options) { await requireFGA(options); } /** * Require fine-grained authorization for a resource. * * No-op if no FGA provider is configured. When FGA is configured, a missing * user fails closed. */ async function requireFGA(options) { const { fgaProvider, user, resource, permission, context, requestContext, metadata, actor } = options; if (!fgaProvider) return; const fgaContext = mergeFGAContext({ context, requestContext, metadata }); const license = getSafeLicenseSummary(); if (isActorSignal(actor)) { const tenantOrganizationId = fgaContext?.requestContext?.get("organizationId"); if (typeof tenantOrganizationId !== "string" || tenantOrganizationId.length === 0) throw new FGADeniedError(user, resource, permission, "trusted actor requires organizationId / tenant scope"); const sourceWorkflow = (actor === true ? void 0 : actor.sourceWorkflow) ?? (typeof fgaContext?.metadata?.["sourceWorkflow"] === "string" ? fgaContext.metadata["sourceWorkflow"] : void 0); const providerEnforced = typeof fgaProvider.requireActor === "function"; if (providerEnforced) await fgaProvider.requireActor(actor, { resource, permission, ...fgaContext ? { context: fgaContext } : {} }); try { captureEEEvent("ee_feature_used", license.anonymousId || getEETelemetryFallbackDistinctId(), { feature: "fga", actor_kind: "system", actor_authorized_by: providerEnforced ? "provider" : "bypass", resource_type: resource.type, resource_id: resource.id, permission, user_id: null, organization_membership_id: null, source_workflow: sourceWorkflow, license_valid: license.valid, license_hash: license.licenseHash, is_dev_environment: license.isDevEnvironment }); } catch {} return; } if (!user) throw new FGADeniedError(user, resource, permission, "authenticated user is required"); await fgaProvider.require(user, fgaContext ? { resource, permission, context: fgaContext } : { resource, permission }); try { captureEEEvent("ee_feature_used", user?.id || license.anonymousId || getEETelemetryFallbackDistinctId(), { feature: "fga", actor_kind: "user", resource_type: resource.type, resource_id: resource.id, permission, user_id: user?.id ?? null, organization_membership_id: user?.organizationMembershipId ?? null, license_valid: license.valid, license_hash: license.licenseHash, is_dev_environment: license.isDevEnvironment }); } catch {} } /** * Error thrown when an FGA authorization check is denied. */ var FGADeniedError = class extends Error { user; resource; permission; status; constructor(user, resource, permission, reason) { const userId = user?.id || user?.workosId || "unknown"; const permissionLabel = Array.isArray(permission) ? `any of [${permission.join(", ")}]` : permission; super(reason ? `FGA authorization denied: ${reason}` : `FGA authorization denied: user ${userId} cannot ${permissionLabel} on ${resource.type}:${resource.id}`); this.name = "FGADeniedError"; this.user = user; this.resource = resource; this.permission = permission; this.status = 403; } }; /** * Default role definitions for Studio. * * These roles provide a sensible starting point for most applications: * - **owner**: Full access to everything * - **admin**: Manage agents, workflows, and users * - **member**: Execute agents and workflows, read-only settings * - **viewer**: Read-only access * * Permission patterns: * - `*` - Full access to everything * - `resource:*` - All actions on a specific resource * - `*:action` - An action across all resources (e.g., `*:read` for read-only) */ const DEFAULT_ROLES = [ { id: "owner", name: "Owner", description: "Full access to all features and settings", permissions: ["*"] }, { id: "admin", name: "Admin", description: "Manage agents, workflows, and team members", permissions: [ "*:read", "*:write", "*:execute", "*:publish", "*:share" ] }, { id: "member", name: "Member", description: "Execute agents and workflows", permissions: ["*:read", "*:execute"] }, { id: "viewer", name: "Viewer", description: "Read-only access", permissions: ["*:read"] } ]; /** * Get role by ID from default roles. * * @param roleId - Role ID to find * @returns Role definition or undefined */ function getDefaultRole(roleId) { return DEFAULT_ROLES.find((role) => role.id === roleId); } /** * Resolve all permissions for a set of role IDs. * * Handles role inheritance and deduplication. * * @param roleIds - Role IDs to resolve * @param roles - Role definitions (defaults to DEFAULT_ROLES) * @returns Array of resolved permissions */ function resolvePermissions(roleIds, roles = DEFAULT_ROLES) { const permissions = /* @__PURE__ */ new Set(); const visited = /* @__PURE__ */ new Set(); function resolveRole(roleId) { if (visited.has(roleId)) return; visited.add(roleId); const role = roles.find((r) => r.id === roleId); if (!role) return; for (const permission of role.permissions) permissions.add(permission); if (role.inherits) for (const inheritedRoleId of role.inherits) resolveRole(inheritedRoleId); } for (const roleId of roleIds) resolveRole(roleId); return Array.from(permissions); } /** * Compound resource keys that expand to a set of per-family resources. * A granted `stored:<action>` is treated as matching any `stored-<family>:<action>` * (and `stored:*` matches any `stored-<family>:*`). */ const RESOURCE_EXPANSIONS = { stored: [ "stored-agents", "stored-mcp-clients", "stored-prompt-blocks", "stored-scorers", "stored-skills", "stored-workspaces" ] }; /** * Check if a permission matches (including wildcard support). * * Permission format: `{resource}:{action}[:{resource-id}]` * * Examples: * - `*` matches everything * - `agents:*` matches `agents:read`, `agents:read:my-agent` * - `*:read` matches `agents:read`, `workflows:read` (action across all resources) * - `agents:read` matches `agents:read`, `agents:read:my-agent` * - `agents:read:my-agent` matches only `agents:read:my-agent` * - `agents:*:my-agent` matches `agents:read:my-agent`, `agents:write:my-agent` * * @param userPermission - Permission the user has * @param requiredPermission - Permission being checked * @returns True if permission matches */ function matchesPermission(userPermission, requiredPermission) { if (userPermission === "*") return true; const grantedParts = userPermission.split(":"); const requiredParts = requiredPermission.split(":"); const expandedFamilies = RESOURCE_EXPANSIONS[grantedParts[0] ?? ""]; if (expandedFamilies && expandedFamilies.includes(requiredParts[0] ?? "")) return matchesPermission([requiredParts[0], ...grantedParts.slice(1)].join(":"), requiredPermission); if (grantedParts.length < 2 || requiredParts.length < 2) return userPermission === requiredPermission; const [grantedResource, grantedAction, grantedId] = grantedParts; const [requiredResource, requiredAction, requiredId] = requiredParts; if (grantedResource === "*") { if (grantedAction === "*") { if (grantedId === void 0) return true; return grantedId === requiredId; } if (grantedAction !== requiredAction) return false; if (grantedId === void 0) return true; return grantedId === requiredId; } if (grantedResource !== requiredResource) return false; if (grantedAction === "*") { if (grantedId === void 0) return true; return grantedId === requiredId; } if (grantedAction !== requiredAction) return false; if (grantedId === void 0) return true; return grantedId === requiredId; } /** * Check if a user has a specific permission. * * @param userPermissions - Permissions the user has * @param requiredPermission - Permission being checked * @returns True if user has the permission */ function hasPermission(userPermissions, requiredPermission) { return userPermissions.some((p) => matchesPermission(p, requiredPermission)); } /** * Resolve permissions from user roles using a role mapping. * * This function translates provider-defined roles (from WorkOS, Okta, etc.) * to Mastra permissions using a configurable mapping. * * @example * ```typescript * const roleMapping = { * "Engineering": ["agents:*", "workflows:*"], * "Product": ["agents:read"], * "_default": [], * }; * * // User has "Engineering" and "QA" roles * const permissions = resolvePermissionsFromMapping( * ["Engineering", "QA"], * roleMapping * ); * // Result: ["agents:*", "workflows:*"] (QA is unmapped, gets _default) * ``` * * @param roles - User's roles from the identity provider * @param mapping - Role to permission mapping * @returns Array of resolved permissions */ function resolvePermissionsFromMapping(roles, mapping) { const permissions = /* @__PURE__ */ new Set(); const defaultPerms = mapping["_default"] ?? []; for (const role of roles) { const rolePerms = mapping[role]; if (rolePerms) for (const perm of rolePerms) permissions.add(perm); else for (const perm of defaultPerms) permissions.add(perm); } return Array.from(permissions); } /** * Static RBAC provider. * * Supports two modes: * 1. **Role definitions**: Use Mastra's native role system with structured roles * 2. **Role mapping**: Directly map provider roles to permissions * * @example Using role definitions (Mastra's native system) * ```typescript * const rbac = new StaticRBACProvider({ * roles: DEFAULT_ROLES, * getUserRoles: (user) => [user.role], * }); * ``` * * @example Using role mapping (for external providers) * ```typescript * const rbac = new StaticRBACProvider({ * roleMapping: { * "Engineering": ["agents:*", "workflows:*"], * "Product": ["agents:read", "workflows:read"], * "_defa