# Agent Setup Prompt
The agent setup prompt is an HTML banner shown **above the bash setup commands**
on the `/setup` page. It is intended for organisation-specific operational notes
that every new analyst should read before running the bootstrap script —
for example: VPN requirements, support channel, data classification reminder,
or platform-specific prerequisites.
## Default behaviour
No banner is shown by default. The `/setup` page renders only the standard
install steps until an admin configures an override.
## Customising per instance
Admins configure the banner via:
- **Admin UI:** `/admin/agent-prompt` — Jinja2 HTML editor with a placeholder
cheatsheet, live preview, and save/reset actions.
- **REST API:**
- `GET /api/admin/welcome-template` — returns `{content, updated_at, updated_by}`.
`content` is `null` when no override is set (default = no banner).
- `PUT /api/admin/welcome-template` with body `{"content": "..."}` — validates
Jinja2 syntax and renders against a stub context before persisting.
Returns `400` on syntax errors or unknown placeholders.
- `DELETE /api/admin/welcome-template` — clears the override; no banner shown.
- `POST /api/admin/welcome-template/preview` with body `{"content": "..."}` —
renders arbitrary content against the calling admin's live context without
persisting. Used by the editor's Preview button.
The override lives in `system.duckdb` (table `welcome_template`, singleton
row id=1). The `DELETE` endpoint NULLs `content`; the audit trail
(`updated_at`, `updated_by`) is preserved.
## Template language
[Jinja2](https://jinja.palletsprojects.com/) with `autoescape=False` and
`StrictUndefined`. Autoescape is off because the rendered output is composed
into the surrounding `/setup` template which applies HTML escaping where
needed via `| e`; doubling the escape would corrupt characters like `&` or `<`
inside code blocks. All four render sites (PUT validation, preview endpoint,
`/setup` page render, `render_agent_prompt_banner`) share the same setting,
so the editor's preview matches what analysts see live. Any typo in a
placeholder name raises an error at PUT validation time rather than silently
emitting an empty string — the editor reports the error immediately so the
admin can fix it before saving.
## Available placeholders
| Placeholder | Type | Notes |
|---|---|---|
| `instance.name` | string | `instance.name` in `instance.yaml` |
| `instance.subtitle` | string | `instance.subtitle` in `instance.yaml` |
| `server.url` | string | Full server URL at render time |
| `server.hostname` | string | Host part only |
| `user` | object or `null` | `null` for anonymous `/setup` visitors |
| `user.id` | string | Authenticated user ID |
| `user.email` | string | Authenticated user email |
| `user.name` | string | Authenticated user display name |
| `user.is_admin` | bool | Whether the user is in the Admin group |
| `user.groups` | list[str] | User's group names |
| `now` | datetime (UTC, tz-aware) | Server time at render |
| `today` | string (`YYYY-MM-DD`) | Server date |
**Anonymous visitors:** `user` is `null` on `/setup` when the visitor is not
signed in. Guard any user-specific content with `{% if user %}…{% endif %}`.
## Security
Output is HTML-sanitized after Jinja2 render as a defense-in-depth measure:
- `` blocks are stripped.
- `` elements are stripped.
- `on*=` event handler attributes (e.g. `onclick=`, `onload=`) are stripped.
- `javascript:` and `data:` URI schemes in `href`/`src`/`action` attributes
are replaced with `#`.
Admins are trusted, but this prevents accidental XSS from copy-pasted snippets
reaching the public `/setup` page.
## Example: VPN and support banner
```html
Before you start: This server is on the corporate VPN.
Connect to vpn.example.com before running the install command.
{% if user %}
Signed in as {{ user.email }} —
open a ticket if you need help.
{% endif %}
```
## Resetting to no banner
Click **Reset to default** in the admin UI, or call
`DELETE /api/admin/welcome-template`. The `/setup` page will show only the
standard install steps with no banner above them.