Skip to main content

Mecatl Studio web UI

Early access: expect breaking changes between releases

Studio is under active development. Its interface, its /api/v1 endpoints, its configuration variables, and the data it keeps in your browser can change in any release, with no deprecation period and no migration path. A screen, a URL, or a setting you rely on today may be renamed or removed in the next version.

Deploy it by an exact version tag rather than latest, read the release notes before upgrading, and do not build an integration against its HTTP surface yet. The published image carries org.stacklok.mecatl.studio.stability=early-access so a deployment can assert on it.

Mecatl Studio is the browser client for a Mecatl deployment. It runs as one container that serves the web app and a backend for frontend (BFF) from the same origin. Your browser talks only to the BFF; the BFF holds your credential and talks to mecated or mecak8s over the same gRPC listener the terminal client uses. Studio never receives a provider API key.

Every release includes a multi-architecture image for linux/amd64 and linux/arm64:

ghcr.io/stacklok/mecatl/studio:<VERSION>

The image is signed with keyless Cosign, carries an SPDX SBOM attestation, and has SLSA build provenance. Verify the signature and the provenance before you deploy it:

cosign verify \
--certificate-identity https://github.com/stacklok/mecatl/.github/workflows/release.yml@refs/tags/<VERSION> \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/stacklok/mecatl/studio:<VERSION>

gh attestation verify oci://ghcr.io/stacklok/mecatl/studio:<VERSION> \
--repo stacklok/mecatl \
--signer-workflow stacklok/mecatl/.github/workflows/release.yml \
--source-ref refs/tags/<VERSION>

Both commands accept only a signature that the release.yml workflow in stacklok/mecatl produced from the <VERSION> tag. To deploy exactly the image you verified, reference it by digest, ghcr.io/stacklok/mecatl/studio@sha256:<DIGEST>; docker buildx imagetools inspect ghcr.io/stacklok/mecatl/studio:<VERSION> prints the digest.

What Studio offers​

Studio exposes the shared capabilities described in the feature guides, and it hides or disables what the connected deployment does not enable:

  • Chats: sessions with streamed runs, image and text-file attachments, permission approvals, steering, and model and reasoning-effort selection.
  • Scheduled: scheduled tasks with a cron builder and run history, when the deployment enables scheduling. See Manage schedules in Studio for what the dialog can and can't change.
  • Skills: configured and learned skills, learning proposals, and session reflection, when the deployment enables them.
  • Settings: personal browser preferences and memory decisions alongside read-only facts about the connected deployment, its providers, and its models.

A global search palette and a keyboard-shortcuts reference page complete the set. Open /workspace/shortcuts directly or follow Keyboard shortcuts from Settings > About. The page lists the current browser bindings and features enabled by the connected deployment. The Enter and Shift+Enter rows follow your Messages while the agent works preference in Settings > Personalise.

Choose execution for a new chat​

Before sending the first prompt, open Execution in the chat composer (or Composer options on a phone). Deployment default uses your server's configured workspace; No files or built-in Shell creates a file-less chat. If the server advertises template discovery, you can select an eligible template by its display name, ID, and pinned revision. The server checks your access again at creation; selection does not reserve capacity. Studio never shows image names, paths, or credentials from the execution recipe.

A disabled catalog still lets you choose default or none. An empty catalog means no templates are eligible for your account; an unavailable catalog can be retried later. If a selected revision disappears, refresh the list and choose another: Studio does not replace that choice with a default. Capacity failures likewise leave your draft unsent. A chat's displayed files and built-in Shell status comes from the bound session's capabilities, not from the template label or the server-wide Shell setting. Diagnostic chats always choose none.

For an operator's image smoke test, select the exact ID and revision supplied by the operator. After the run finishes, reload Studio and reopen the same chat from history to check retained files. Creating a new chat selects a new session; it does not reattach the workspace of the chat you tested. See the deployed-image smoke test for the file and utility checks.

Inspect delegated activity​

Browse and review skills​

Open Skills to browse the deployment's configured skills. Sort the table by name or description, or filter by name, description, or owner. On screens narrower than 500 pixels, the table becomes a list. If the deployment disables the Skill tool, a banner explains that the agent loads no skills.

Select a skill to see its description and slug. Agent-owned skills also show the owner and active version.

The Files section lists the skill's files with their text, highlighted by file type, each in its own scrolling block. The first entry is the skill's instructions: the text after the frontmatter, not the raw SKILL.md, so frontmatter fields such as allowed-tools are not shown. An agent-owned skill has only that entry. Studio loads the first 32 files and says how many more the skill has. A file over 25,000 bytes, or one that is not text, stays in the list with a reason in place of its text.

When the deployment enables learned skills, open the Learned view to see agent-drafted versions and their states. If you follow a direct link to this view when learned skills are disabled, Studio explains why it is unavailable.

Select a version to review its body, evidence count, and diff against the version it replaces. The review dialog offers the actions currently available for that version: Activate, Reject, Roll back, or Archive. Each action asks for confirmation. If the skill has changed since the page loaded, Studio reports an error and refreshes the dialog with the current state.

Recent changes shows the 20 most recent lifecycle events. The learned-skills list displays only the first 100 skills.

Review learning suggestions​

When the deployment enables learning proposals, open Settings > Learning to review what the agent suggests remembering. Filter the list by Pending, Deferred, Approved, or Rejected, and select Load more to reach suggestions past the first page. Select Details to see where a suggestion came from: a link to each source chat, whether the source is still available, and the excerpt the agent read.

Approve, Reject, and Undo approval each ask for confirmation. If a suggestion changed since the page loaded, Studio refreshes the list and asks you to review it again. A deferred procedure can be approved as a learned-skill draft.

When the deployment enables reflection, Learn from a chat asks the agent to re-read a finished chat and add any suggestions to the list. The deployment manages the learning mode and sensitivity; Studio does not change them.

Client guidance​

Studio explains settings ownership in the settings pages: personal choices stay in the browser, while deployment-managed provider, model, posture, and storage facts are read-only. Settings > Personalise holds appearance, chat, and notification preferences, and explains theme and palette choices. Settings > Permissions shows the deployment's safety level without a way to change it. Delegated-activity panels explain their observed history and incomplete replay. If the deployment reports degraded session storage, a notice above the navigation says that some chats may be missing; Settings > Storage shows the details. About identifies the Studio image and installed SDK in one card and the connected daemon in another. In a published image, the SDK version ends with the Studio release it was built from, after a +, such as 0.5.0+v0.0.48. Images built without a release tag show only the SDK package version. Ask users for Copy support summary when diagnosing a deployment issue.

Install Studio in your browser​

Open your Studio URL over HTTPS in a browser that offers app installation, then choose the browser's Install app command. On a local workstation, the loopback URL in the Compose setup also qualifies. Launch the installed Studio from your app list; it opens Chats at /workspace/chat.

Where the browser supports sharing to installed apps, Studio appears as a share target. Shared text opens a new chat with the text in the composer, as a ?prompt= link does, and never sends it. Studio receives only the shared text; a title or link that the sharing app sends separately is not included.

The browser puts shared text in the page address, so its length is limited. Studio accepts an address and headers of up to 16 KB, which fits about 14,000 characters of English text but only about 1,600 characters of Chinese or Japanese text. An ingress in front of Studio can set a lower limit; ingress-nginx, for example, refuses addresses over 8 KB by default. When shared text is too long, Studio shows a page that says so instead of a chat; copy the text and paste it into a new chat's message box.

The installed window loads Studio from its origin and needs a connection to the BFF and Mecatl deployment for agent actions. A loaded window reports an unavailable connection if that link drops. For a fully offline launch, the browser handles the missing connection; Studio does not provide an offline starting page.

Use a chat​

Open Chats, write a message, and select Send. The row under the composer holds the chat's settings: Mode (the permission mode), Model (the model and its reasoning effort), and, before a chat starts, Execution (see Choose execution for a new chat). On a phone, the same settings are under Composer options, the sliders button. In an existing chat, changing the mode applies in place; choosing another model or effort creates a fork of that chat. The row also shows how much of the model's context the chat uses, with Compact when the deployment allows it. The chat header shows the mode when it is Accept edits or Plan. Press Enter to send or Shift+Enter for a new line. Enter used to confirm an input method candidate leaves the message in the composer.

Studio keeps each chat's unsent text in this browser tab, so it is still there after a reload or when you come back from another chat. Attachments are not kept. Clear draft, the × beside Send, empties the composer.

Starter prompts fill the composer for you to review. To hide them on a new chat, turn off Starter prompts in Settings > Personalise; the choice belongs to your account in this browser. A link with ?prompt= also fills it without starting a run. If the link includes send=1, Studio shows the prompt, target chat, model, and permission mode before you select Send prompt or Edit prompt. It also shows Execution: the execution selected for a new chat, or whether files and the built-in Shell are available in an existing chat. Opening or reloading the link never sends it automatically, and the prompt does not replace your saved draft until you select Edit prompt. If you need to sign in first, keep the original tab open. Its prompt stays there while the popup or new tab completes sign-in.

Use Attach files, or drop files on the composer, to add attachments. If the selected model supports images, you can add up to 16 images to one message. Each image can be at most 10 MiB, with a combined limit of 20 MiB. Text files of up to 256 KiB each, and 512 KiB together, are added to the message text, each between --- attached file: <name> --- and --- end of <name> --- lines. When an attached file has three or more dashes in a row, these lines use one more dash than the longest such run, so file text can't end its block early. Studio refuses binary and audio files. You can select the microphone button to dictate in a browser that supports speech recognition on a secure origin. Review or edit the recognized text before sending. If recognition is unavailable or microphone access fails, Studio explains the failure and keeps the typed draft available.

Follow and control a run​

The transcript streams messages with Markdown, code blocks, and tool activity. Saved inline images appear in the transcript; an external image URL appears as a link you can choose to open. You can scroll back to read earlier messages and use Scroll to latest message to resume following the stream. The status distinguishes sending, working, waiting for approval, and a recorded completed, stopped, canceled, or failed result. If a stream closes without a recorded result, Studio reports an unknown outcome and checks saved history when you reconnect. A history gap does not claim that the run finished.

While Mecatl is working, the composer can queue a text message for the next run or steer the active run. Your Interface preference chooses what Enter does; Shift+Enter selects the other action. Queued messages wait above the composer. Each one's actions menu can edit or delete it, or steer it into the active run. A steered message leaves the queue only when the run accepts it, and the queue never sends it as the next run while the steer is on its way. Use Stop to cancel the active run. If a run fails and offers Retry, that action retries the failed run without sending the prompt again. Image messages and text-file attachments can be sent after the current run ends.

Review tool calls and plans​

When a tool needs permission, review its name, reason, and arguments in the approval card. Edit and Write cards show a line diff with display limits when the arguments support one; commands show their text, with any timeout or temporary-storage scope, and other arguments show formatted JSON. Arguments that repeat a name, spell one in another case, or add one the tool doesn't define also show as formatted JSON, because the tool could read them differently from a diff or command view. Select Raw arguments to see the exact arguments, then choose Allow once, Always allow, or Deny. If the arguments are unavailable, only Deny is enabled. In a chat, Open in detail panel shows the same request in the side panel with its complete diff, and you can decide there too.

On the chat's active request, with focus in its card or detail panel, press Y or A to allow it once, W to always allow it, or N or D to deny it. The arrow keys move between the verdict buttons. A new request never takes focus, so a key you type as it arrives answers nothing: press Tab or click to move focus into its card first. These keys never act from outside the request's card or panel, while you type in the composer or another field, while a verdict is being sent, or when its outcome is uncertain. Escape denies the active request; in a detail panel showing a different request, Escape closes the panel instead.

The card is red when a word in the tool name is delete, remove, drop, revoke, destroy, purge, rm, or rmdir, in any naming style, such as delete_file or RemoveBranch. Otherwise it's amber. For an MCP tool, named mcp__<server>__<tool>, only the tool part counts, so the server name doesn't turn every tool from that server red. The color comes from the tool name only, so review the arguments of every request.

A Plan review card shows the proposed plan and any accompanying note. Choose Approve & run, Auto-accept edits, or Iterate. Iterate rejects the plan and keeps the chat in plan mode. The connected server must support exact plan review for these controls to be available. Otherwise, Studio explains why the review is read-only. If Studio cannot confirm a tool or plan decision, select Refresh activity before deciding again.

Each press of Escape handles one action. An open dialog or menu closes first and returns focus to its trigger. Otherwise, Escape acts in this order:

  1. Clear a text selection.
  2. Deny a pending tool request or select Iterate for a plan review, when the review controls are available.
  3. Close a side panel.
  4. Stop the active run.

Escape stops at a pending review even when its controls are unavailable.

If none of these actions applies and you have an unsent draft, press Escape, release it, then press it again within half a second to clear the draft. The visible hint shows the available action. Pressing Enter or Space on a focused approval button selects that decision without sending your draft. The Keyboard shortcuts page lists these bindings.

Review external authorization​

If a tool needs external authorization, the chat header shows Waiting for authorization; on a phone, where the header has no room for the label, an amber key replaces the spinner that marks a working run. Select Review authorization in the chat. Select Open authorization to complete the external step in a new tab, then return to Studio and select Recheck. The panel shows the observed status. Cancel authorization ends the pending handoff. Opening the external page alone does not grant access, and closing the review panel leaves the handoff pending. Open the page from the Studio panel. The Studio authorization link rejects address-bar navigation and links from other sites.

If Studio cannot confirm a Recheck or Cancel authorization result, the panel disables both actions. Select Refresh activity to check for a later status from the same handoff. A new pending status restores the actions; a resolved status updates the panel. If no later status appears, the outcome remains uncertain and the actions stay disabled.

Folders, queued messages, unsent drafts, and account preferences survive a reload for the same account. Studio clears them when you sign out or switch accounts. The device theme and palette remain.

Return to a chat​

Generated chat titles appear in both the chat list and header when Studio receives a newer title, even if generation finishes after the run. A later operator rename takes precedence over an older generated title. If you return to the same open chat after its tab was hidden for at least 20 seconds, Studio refreshes its state and briefly reports only what it can verify, such as a run still working or the chat waiting for approval. Reloading the page does not create a return notice.

When a scheduled task delivers a start or completion note to this chat, the transcript shows the recorded note with its schedule and fire attribution. The note body appears as plain text. A task's fire history alone does not add a note to the chat. An open, visible chat checks saved history while idle, so a short delivery's reply and turns added from another client appear even when their live activity was missed.

In a long chat, use the numbered Message minimap beside the transcript to jump to a message. Its buttons work with a keyboard and remain available on a small screen. Select Scroll to latest message when you want to follow new messages again.

Select Reply in thread under a saved message to open a separate chat in the side panel. The thread copies the source chat's full history at the time you fork it, including messages after the one you selected. The selected message anchors the thread in the source transcript. Each occurrence has its own thread, even when two messages have the same text.

Select an image or tool result in the transcript to inspect its preview in the same side panel. Select Canvas in the chat header to edit Markdown notes and switch between Edit and Preview. On a phone, an open chat has it under Chat options > Open local canvas instead. Canvas notes stay in this browser for your account and the selected chat. Opening or previewing them does not send their text to Mecatl.

Inspect a session​

Open Chat options > Inspect session to see its kind, state, model, usage, and available placement details. View transcript shows the saved messages in order and identifies an incomplete transcript. Inspect soul shows the resolved soul for your connection; it is shared across the sessions on that connection. Studio explains when any inspection view is unavailable.

The session list has a separate Inspect-only sessions group. Opening one shows Session details and, when permitted, its saved transcript. It has no chat composer or run controls. Copy session ID appears when permitted.

For a chat with eligible worktrees, select Choose worktree, choose a listed worktree, and confirm Fork in selected worktree or Clear in selected worktree. Studio opens the new session after the operation; the source chat keeps its placement. If the choice has become unavailable, Studio refreshes the list so you can choose again. Debug with AI first asks you to confirm that stored transcript and event evidence will be sent to the model in a separate diagnostic chat. The inspected session remains read-only to that debugger.

Try it locally with Compose​

The repository's apps/docker-compose.yml starts a mecated container and Studio on an internal network and publishes Studio on 127.0.0.1:3100.

  1. Clone the repository and change into apps/.

  2. Copy .env.example to .env and set one provider key, for example ANTHROPIC_API_KEY.

  3. Run:

    docker compose up --build
  4. Open http://127.0.0.1:3100.

The local mecated runs without OIDC, so Studio starts in its unauthenticated mode and every browser that reaches it acts as the same principal. The Compose file opts into that with STUDIO_ALLOW_UNAUTHENTICATED=1 because it publishes Studio on the loopback address only.

Deploy against mecak8s​

In production Studio runs behind your ingress and connects to a mecak8s deployment that publishes an OIDC profile (see Cloud-native k8s with mecak8s). Users sign in through the deployment's identity provider with Authorization Code and PKCE; Studio keeps the session in encrypted, HttpOnly cookies and forwards the user's token on every request.

Set these variables on the Studio container:

VariableValue
MECATL_BASE_URLThe https:// address of the mecak8s gRPC listener, for example https://mecak8s.example.com.
STUDIO_PUBLIC_URLThe origin your users open, for example https://studio.example.com. Studio derives the OIDC callback https://studio.example.com/api/v1/auth/callback and the Secure cookie flag from it.
STUDIO_SESSION_SECRETAt least 32 bytes, shared by every Studio replica. Generate one with openssl rand -base64 48.
STUDIO_TRUSTED_PROXY_HOPS1 when one ingress or load balancer sits in front of Studio, so rate limiting and audit records see the client address from X-Forwarded-For instead of the proxy.

Register the callback URL with your identity provider's public client. Studio discovers the issuer, client ID, audience, and scopes from the deployment's protected-resource document and refuses to start when that document cannot be fetched, so a misconfigured target fails at deploy time.

MECATL_RESOURCE_URL is the deployment's canonical resource identifier: the exact value its protected-resource document publishes as resource. Studio fetches the document from that URL's /.well-known/oauth-protected-resource path and refuses to start when the published resource differs from it. Set it only when that identifier is not the gRPC address in MECATL_BASE_URL. The local Compose file sets it because mecated serves gRPC and HTTP on separate ports.

Studio answers only requests whose Host is the host of STUDIO_PUBLIC_URL, so the ingress in front of it must pass the original Host header through.

Studio listens on port 3100 on every interface inside the image, answers GET /api/health on any host name as soon as it starts, and runs as non-root user 65532. It writes nothing to its own filesystem, so run it with a read-only root filesystem, no Linux capabilities, and privilege escalation disabled. In a Kubernetes pod, set this container securityContext:

securityContext:
runAsNonRoot: true
runAsUser: 65532
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]

Scale Studio horizontally without shared storage: every replica needs only the same STUDIO_SESSION_SECRET.

Install with Helm​

The mecatl-studio chart at oci://ghcr.io/stacklok/mecatl/charts/mecatl-studio deploys Studio with the settings above on Kubernetes 1.30 or later. Each chart version matches a Mecatl release after v0.0.47 and pulls that release's Studio image. Choose a version from GitHub releases, then substitute its number without the leading v for <CHART_VERSION>.

  1. Create the session secret in the namespace Studio runs in:

    kubectl create namespace mecatl-studio
    kubectl create secret generic mecatl-studio-session \
    --namespace mecatl-studio \
    --from-literal=STUDIO_SESSION_SECRET="$(openssl rand -base64 48)"
  2. Install the chart and wait for the rollout:

    helm upgrade --install mecatl-studio \
    oci://ghcr.io/stacklok/mecatl/charts/mecatl-studio \
    --version <CHART_VERSION> \
    --namespace mecatl-studio \
    --set mecatl.baseURL=https://mecak8s.example.com \
    --set publicURL=https://studio.example.com \
    --set session.secretName=mecatl-studio-session
    kubectl rollout status deployment/mecatl-studio --namespace mecatl-studio

A release named mecatl-studio keeps that name for its Deployment and Service; any other release name produces <RELEASE>-mecatl-studio. Set fullnameOverride for a fixed name.

The chart is signed with keyless Cosign under the same release identity as the image:

cosign verify \
--certificate-identity https://github.com/stacklok/mecatl/.github/workflows/release.yml@refs/tags/v<CHART_VERSION> \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/stacklok/mecatl/charts/mecatl-studio:<CHART_VERSION>

The chart checks required settings and their combinations before anything reaches the cluster: it refuses to render without mecatl.baseURL or publicURL, with a static token but no allowUnauthenticated, or with an extraEnv entry that overrides a variable the chart sets. Checks that depend on Secret contents, such as the session secret's length, still fail at startup.

To publish Studio through a Gateway API listener, set httpRoute.enabled=true and httpRoute.parentRefs. The route's host name defaults to the host of publicURL. The chart trusts one proxy hop in X-Forwarded-For by default, so set networkPolicy.ingressFrom to the gateway's namespace or pods; otherwise any pod that reaches Studio directly can choose the client address Studio rate-limits and audits. A NodePort or LoadBalancer Service requires trustedProxyHops=0.

The chart's NetworkPolicy allows egress to DNS in kube-system and to TCP port 443 and the ports in mecatl.baseURL and mecatl.resourceURL. Set networkPolicy.dns when your cluster resolves names elsewhere, such as NodeLocal DNSCache or OpenShift's openshift-dns. Add networkPolicy.extraEgress rules for an identity provider on a port other than 443, and when a Service or in-cluster gateway in front of Mecatl forwards to another port, because NetworkPolicy matches the destination pod's port.

Run helm show values oci://ghcr.io/stacklok/mecatl/charts/mecatl-studio for every value.

Sign in and recover​

On your first visit, select Sign in to Mecatl in the centered card. If the card reports an outage, select Try again after the connection returns. When a session expires in an open workspace, use Sign in in the status banner. Studio keeps the original tab, route, and unfinished draft open while the popup completes login. If the browser blocks the popup or you close it, use Retry or the manual new-tab sign-in link. Return to the Studio tab after signing in through the new tab so it can check your session.

If your session expires while Studio is open, sign in again. Studio refetches reads after you return to the same account. Retry a write or stream yourself because the first request may have reached the deployment before its response was lost. If you sign in to a different account, Studio clears the previous account's browser data and unfinished draft before opening that workspace.

The connection banner shows a Studio or deployment outage ahead of a sign-in prompt. A pending connection check is neutral. When Studio cannot verify your session, use the banner's retry action; the current route and draft stay in place while it checks again.

Static token or no authentication​

Studio can also run as a single service identity by setting MECATL_AUTH_TOKEN together with MECATL_BASE_URL, or against a deployment that publishes no OIDC profile. In both cases every browser that reaches Studio acts as one principal, so the image refuses to start unless you also set STUDIO_ALLOW_UNAUTHENTICATED=1. Use this only behind your own access control.

Configuration reference​

VariableDefaultPurpose
MECATL_BASE_URLunsetConnects to an existing Mecatl gRPC listener. The image requires it.
MECATL_RESOURCE_URLderived from MECATL_BASE_URLCanonical RFC 9728 resource identifier. It must equal the resource the deployment publishes.
MECATL_AUTH_TOKENunsetStatic bearer token; disables browser login.
STUDIO_PUBLIC_URLunsetBrowser-facing origin; required for browser login and inside the image.
STUDIO_SESSION_SECRETunsetSecret sealing the session cookies; required for browser login.
STUDIO_PORT3100Listening port.
STUDIO_HOST0.0.0.0 in the image, 127.0.0.1 elsewhereListening address.
STUDIO_TRUSTED_PROXY_HOPS0Reverse-proxy hops to trust when reading X-Forwarded-For.
STUDIO_RATE_LIMIT_MAX20Sign-in requests (login, callback, and logout) allowed per client within the window. The session check the browser makes on every page load is not counted.
STUDIO_RATE_LIMIT_WINDOW_MS60000Rate-limit window in milliseconds.
STUDIO_ACTIVITY_REPLAY_MAX2000Durable events one reattach replays before it tells the browser to read the transcript for older history. Live events are never bounded.
STUDIO_ACTIVITY_MAX_STREAMS4Concurrent activity streams one chat admits per replica.
STUDIO_ALLOW_UNAUTHENTICATEDunsetSet to 1 to run a static-token or no-authentication target inside the image.
STUDIO_LOG_LEVELinfoOne of debug, info, warn, or error. Logs are JSON lines on standard error.

For local development outside a container, the Studio README covers the spawn and mock runtime modes and the task studio:* commands.

Next steps​

Troubleshooting​

Studio shows "Page not found" or "Something went wrong"

Use Go to Chats to return to the workspace. If a page reports an unexpected error, choose Try again first. If Studio cannot render its main interface, choose Reload Studio. Check the Studio URL if the page remains missing.

Studio exits with "authentication setup failed (auth_discovery_failed)"

Studio could not fetch the deployment's protected-resource document. Check that MECATL_BASE_URL (or MECATL_RESOURCE_URL) points at the HTTPS address that serves /.well-known/oauth-protected-resource. A deployment without OIDC answers 404 there, which Studio treats as "no browser login" instead of an error.

Studio exits with "authentication setup failed (auth_discovery_invalid)"

Studio fetched the protected-resource document but refused its contents. The most common cause is a resource value that differs from MECATL_RESOURCE_URL, or from MECATL_BASE_URL when MECATL_RESOURCE_URL is unset. Compare the resource field the deployment publishes with the value Studio uses, including scheme, port, and trailing path. The document must also name exactly one authorization server over HTTPS, accept header bearer tokens, and publish the Mecatl audience and client ID.

Every page answers "Host not allowed" (421)

The request reached Studio with a Host header other than the host of STUDIO_PUBLIC_URL. Configure the ingress or load balancer to preserve the original Host, and make sure users open exactly the STUDIO_PUBLIC_URL origin. Without STUDIO_PUBLIC_URL, Studio answers only on localhost, 127.0.0.1, or [::1].

Sign-in loops back to the login screen

Studio sets Secure cookies when STUDIO_PUBLIC_URL is https://. Confirm users open exactly that origin, that the identity provider redirects to STUDIO_PUBLIC_URL/api/v1/auth/callback, and that every replica shares the same STUDIO_SESSION_SECRET.