Serve a frontend from your agent

Configure an agent that ships its own web UI instead of a chat sidecar
View as Markdown

By default, Astro AI gives your agent a built-in chat UI, Slack adapter, and OIDC-authenticated web view. If you’d rather serve your own web interface — a custom dashboard, a static SPA, a server-rendered app — set agent.interfaces.frontend: true and the platform routes incoming traffic straight to your container.

Before you start

  • An Astro AI account.
  • The ast CLI installed and authenticated.
  • An agent container you can build: a Dockerfile and an HTTP server that listens on port 80.

What frontend: true does

Declaring frontend: true changes how the agent is served in three ways:

  1. No built-in chat UI. The platform skips the chat interface that’s normally attached to your agent.
  2. Traffic routes straight to your container. A dedicated HTTPS hostname is provisioned and routes to your agent on port 80.
  3. --adapter is ignored at deploy time. Adapters (web, insecure-web, slack) only apply to messaging agents. A frontend agent serves whatever your container serves.

Default (messaging agent):

frontend: true:

You give up the built-in chat UI, but keep the OIDC sign-in at the front door. You gain full control over the request/response lifecycle.


Minimal configuration

astropods.yml
1spec: blueprint/v1
2name: my-dashboard
3
4agent:
5 build:
6 context: .
7 dockerfile: Dockerfile
8 interfaces:
9 frontend: true
10 messaging: false

A few rules to know:

  • interfaces must be nested under agent. Putting it at the top level is silently ignored.
  • Omitting interfaces entirely defaults to messaging: true. As soon as you declare interfaces, messaging defaults to false — you have to opt back in if you want both.
  • When frontend: true, your container must listen on port 80 in production. This is enforced by validation rule 15 of the Astropods Spec.

Example agent

A minimal server that returns a static page. Pick your language:

server.js
1import express from "express";
2
3const app = express();
4const PORT = process.env.PORT || 80;
5
6app.get("/", (_req, res) => {
7 res.send(`
8 <!doctype html>
9 <html>
10 <head><title>My Agent</title></head>
11 <body>
12 <h1>Hello from my agent</h1>
13 <p>Served directly from the agent container.</p>
14 </body>
15 </html>
16 `);
17});
18
19app.listen(PORT, "0.0.0.0", () => {
20 console.log(`Listening on :${PORT}`);
21});
Dockerfile
1FROM node:20-alpine
2WORKDIR /app
3COPY package*.json ./
4RUN npm ci --omit=dev
5COPY . .
6EXPOSE 80
7CMD ["node", "server.js"]

Local development on a different port

Most frameworks default to a higher port locally (Express 3000, Vite 5173, FastAPI 8000) and binding to :80 typically requires elevated privileges. Use dev.interfaces.frontend.port so ast project start runs your dev server on its native port; the platform proxies :80 to it.

astropods.yml
1agent:
2 interfaces:
3 frontend: true
4 messaging: false
5
6dev:
7 interfaces:
8 frontend:
9 port: 3000
10 command: bun --watch run start

With this in place, ast project start runs your container on 3000 locally and routes incoming traffic to it. In production the container still serves :80 directly — no proxy involved.

dev.interfaces.frontend.port only affects local dev. In production your container must listen on :80 — otherwise the deployed container crash-loops. If your framework defaults to a different port, set it explicitly (via PORT, a CLI flag, or your entrypoint) so the deployed container binds to :80.


Combining frontend with messaging

Setting both interfaces to true deploys your frontend container and the built-in chat interface. The frontend gets the dedicated hostname; the chat interface handles chat / Slack on its own routes.

astropods.yml
1agent:
2 interfaces:
3 frontend: true
4 messaging: true

Use this when you want a custom UI plus the platform’s built-in Slack integration, for example.


Authentication

The platform protects a frontend agent with the same OIDC sign-in used for the built-in chat interface: by default, visitors sign in to Astro AI at the front door before any traffic reaches your container. The signed-in user’s identity is forwarded to your agent, so you can authorize requests inside the container — see Manually authorize requests.

The platform requires sign-in but does not enforce per-user access rules for a custom frontend; your own server decides what each authenticated user may do once the request arrives.


Deploy

Frontend agents deploy the same way as any other blueprint:

$ast blueprint deploy my-dashboard

After deploy, ast agent list shows the assigned hostname. Open it in a browser to confirm your container is serving traffic.

If your container fails to bind to :80 in production, the deployed container crash-loops. Check ast agent logs <name> and verify the listen port matches the spec.

Next steps