Dashboard
Overview
The ASD Dashboard is a browser-based control panel for monitoring and managing your exposed services. It runs locally at http://asd.localhost/ and is also available as a hosted version at https://dashboard.asd.engineer.
Open the local dashboard by running:
asd net The asd net TUI shows all registered services with their status, URLs, and quick actions. Press Enter on any service to open an actions menu. The browser-based dashboard at http://asd.localhost/ provides the same information in a widget layout where each service gets its own card.
Fragment URL Strategy
The ASD Dashboard uses a privacy-first architecture based on fragment URLs. This is a deliberate design choice that sets it apart from typical dashboards where service data lives on the server.
How It Works
When you open the dashboard or share a service URL, all sensitive data is encoded in the URL fragment (the part after #):
https://dashboard.asd.engineer/#eyJzZXJ2aWNlcyI6W3sibmFtZSI6Im... The fragment is never sent to the server. This is a fundamental property of URLs defined in RFC 3986: browsers do not include the fragment in HTTP requests. The dashboard server receives a request for / and serves the static HTML/JS application. All service data, credentials, and configuration stay entirely in your browser.
What This Means
| Aspect | Traditional Dashboard | ASD Fragment Dashboard |
|---|---|---|
| Service data storage | Server database | Your browser only |
| Server knows your services | Yes | No |
| URL sharing | Requires account | Copy and paste the URL |
| Privacy | Depends on server trust | Cryptographically guaranteed |
| Offline support | No | Yes (cached app shell) |
The backend serves only static assets. It has no database, no user accounts, no session storage. It literally cannot see your service data because the fragment never reaches it.
Sharing Services
To share your service dashboard with a teammate:
- Open
asd netand select a service - Choose “Copy Dashboard URL”
- Send the URL to your teammate
The recipient opens the URL, the fragment is decoded client-side, and they see your service status. No account needed, no server involved.
Encoding Format
The fragment contains a base64-encoded JSON payload:
{
"services": [
{
"name": "frontend",
"localUrl": "http://localhost:5173",
"caddyUrl": "http://app.localhost",
"tunnelUrl": "https://app-abc123.eu1.tn.asd.engineer",
"status": "online"
}
],
"settings": {
"theme": "dark"
}
} This means:
- Bookmarking a dashboard URL preserves the full state
- Sharing a URL shares the exact view
- No synchronization needed between devices
- The server never processes or stores this data
Dashboard Configuration in asd.yaml
Configure the dashboard layout through the hub: section in your asd.yaml:
hub:
globalSettings:
theme: "dark"
hideBoardControl: true
hideViewControl: true
hideServiceControl: false
showMenuWidget: false
views:
showViewOptionsAsButtons: true
viewToShow: "Terminal"
boards:
- name: "Development"
order: 1
views:
- name: "Services"
maxInstances: 10
widgets:
- type: "service"
serviceId: "frontend"
columns: 4
rows: 4
order: "0"
- type: "service"
serviceId: "api"
columns: 4
rows: 4
order: "1" Global Settings
| Property | Type | Description |
|---|---|---|
theme | "dark" or "light" | Dashboard color theme |
hideBoardControl | boolean | Hide the board switcher |
hideViewControl | boolean | Hide the view mode switcher |
hideServiceControl | boolean | Hide per-service action buttons |
showMenuWidget | boolean | Show the navigation menu widget |
Boards and Views
The dashboard organizes services into boards. Each board contains views, and each view contains widgets.
A widget represents one service card in the dashboard:
| Property | Type | Description |
|---|---|---|
type | "service" | Widget type |
serviceId | string | Service ID from your asd.yaml |
columns | number | Widget width in grid columns |
rows | number | Widget height in grid rows |
order | string | Display order |
Local vs Hosted Dashboard
| Feature | Local (asd.localhost) | Hosted (dashboard.asd.engineer) |
|---|---|---|
| Service data | Real-time from Caddy | From fragment URL only |
| Auto-refresh | Yes | No (static snapshot) |
| Actions (start/stop) | Yes | No (read-only) |
| Health checks | Live | Snapshot from share time |
| Account required | No | No |
The local dashboard connects to the Caddy admin API for real-time service data. The hosted dashboard is a read-only viewer that decodes whatever is in the fragment URL.
Upcoming: Team Module
The Team module is an upcoming feature that adds persistent, server-side service sharing for teams. It is the counterpart to the fragment URL strategy.
Fragment URLs vs Team Module
| Aspect | Fragment URLs (current) | Team Module (upcoming) |
|---|---|---|
| Privacy model | Zero-knowledge, backend sees nothing | Server stores service registry |
| Sharing | Copy URL, one-time snapshot | Persistent, real-time team view |
| Authentication | None needed | ASD account + team membership |
| Service updates | Manual re-share | Automatic sync |
| Use case | Quick sharing, demos, privacy-first | Ongoing team collaboration |
What the Team Module Will Add
- Persistent service registry: Team members see each other’s exposed services in real time
- Team dashboard: A shared view at
dashboard.asd.engineerthat shows all team services - Access control: Only team members with the right permissions see services
- Service discovery: Find a teammate’s service by name instead of exchanging URLs
- Audit trail: Track who exposed what and when
The fragment URL approach remains available. It does not go away. The Team module adds an opt-in layer on top for teams that want persistent, synchronized service visibility. When you expose a service with asd expose, you choose whether it appears in your team dashboard or stays private.
Architecture Difference
Fragment URLs:
Your browser <--fragment data--> URL (never reaches server) Team module:
Your CLI --> ASD API --> Team service registry --> Teammate's dashboard The two approaches serve different needs. Use fragment URLs when you want privacy and zero-server-trust. Use the Team module when you want real-time team collaboration.
Iframe Embedding
The dashboard can be embedded in other applications via iframe. Configure the allowed origin in your service definitions:
network:
services:
dashboard:
dial: "127.0.0.1:3000"
iframeOrigin: "https://dashboard.asd.engineer"
deleteResponseHeaders:
- "Content-Security-Policy"
- "X-Frame-Options" The iframeOrigin property sets the X-Frame-Options: ALLOW-FROM <origin> header. Caddy strips conflicting headers from the upstream and replaces them with its own.
Related Guides
- Access Patterns — three ways to reach your services
- Caddy Reverse Proxy — routing and security configuration
- Security and Authentication — protecting exposed services