Setup
Configure identity, tenancy, and the local surfaces before the first send.
A Mursil deployment is three processes plus PostgreSQL and Redis: the migrator, Mursil.Server, and Mursil.Admin. The migrator applies schema and the first-party seed, then exits. The server accepts the HTTP API. The admin portal is the operator UI.
What you set
Configuration uses the standard .NET shape. Nested keys use a colon in JSON and a double underscore in environment variables.
| Setting | Environment variable | What it does |
|---|---|---|
Mursil:MultiTenancy | Mursil__MultiTenancy | Deployment tenancy switch. Default false. |
Identity:Authority | Identity__Authority | OIDC issuer. Required for a published deployment. |
Identity:Audience | Identity__Audience | Expected API audience. The local default is mursil-api. |
Identity:ClientId | Identity__ClientId | Admin portal client id. The local default is mursil-admin-panel. |
Identity:ClientSecret | Identity__ClientSecret | Admin portal client secret. Set it from your secret store. |
Email:Smtp:Host | Email__Smtp__Host | SMTP host. Local compose points this at Mailpit. |
Email:Smtp:Port | Email__Smtp__Port | SMTP port. |
Email:Smtp:EnableSsl | Email__Smtp__EnableSsl | Whether the SMTP connection uses TLS. |
Copy docker/.env.example to docker/.env for the local image and replace the placeholder values there. Do not commit that file, and do not reuse local placeholders in a shared or published deployment.
Database and cache passwords, hashing material, and encryption material belong in the environment or a secret store. The portal never asks you to paste a raw provider secret. Provider connections store a reference such as env:VARIABLE_NAME or secret:path.
Tenancy
Mursil:MultiTenancy defaults to false. In Docker Compose the same switch is MURSIL_MULTI_TENANCY, and it defaults to false.
Leave the switch false while one deployment serves one organization. Callers do not send a tenant id. URLs do not carry one. The portal does not offer a tenant picker. A status chip in the header marks the current scope. It is not a selector.
Set the switch to true only when one deployment must serve many customer organizations. Callers must then supply a tenant id. On the HTTP API that is the tenant_id claim or the X-Mursil-TenantId header. The Mursil.Integration package sends Mursil:Integration:DefaultTenantId, or the id returned by an IMursilIntegrationTenantAccessor, and only when the switch is on.
Identity
Mursil authorizes six roles on the claim type role:
| Role | What it can open |
|---|---|
SystemAdmin | Every portal page, including the full menu. |
TenantOwner | Operator pages, templates, branding, and owner-only actions such as creating a provider connection. |
TenantAdmin | Operator pages, templates, and branding. |
TemplateEditor | Templates and branding. |
Operator | Dashboard, messages, failures, audit, providers, sender domains, and settings. |
Viewer | Dashboard, messages, and trace. |
A published deployment sets MURSIL_DEV_ISSUER=false and Identity:Authority to your issuer. An empty authority does not open the portal. Admin refuses to start until Identity:Authority and Identity:ClientSecret are set. Server validates JWTs against that authority.
The local development issuer is a stand-in for that OIDC server. It is not a production identity provider. Turn it on only for a local run (MURSIL_DEV_ISSUER=true or Mursil:DevIssuer). When it is on, local compose points Server and Admin at https://dev-issuer:8443 and ignores IDENTITY_AUTHORITY. Add 127.0.0.1 dev-issuer to the hosts file so a browser on the host can reach it. The issuer asks you to choose one of the six roles. Choose SystemAdmin when you need the full menu.
Local surfaces
scripts/docker-local.sh builds the local images and starts Postgres, Redis, Mailpit, the migrator, Server, and Admin. With the development issuer enabled it also starts that issuer.
| Surface | Address |
|---|---|
| Server health | http://localhost:8088/health |
| Admin | https://localhost:8089/ |
| Development issuer | https://dev-issuer:8443/ |
| Mailpit | http://localhost:18025/ |
| Mailpit SMTP from the host | localhost:10259 |
| Postgres | localhost:54329 |
| Redis | localhost:63799 |
Inside Compose, Server reaches Mailpit as host mailpit on port 1025. The seeded SMTP connection is named Local SMTP and points at that endpoint. Its settings reference is env:Email:Smtp.
A contributor who is changing source can start the same topology from the Aspire AppHost instead. That path is described in Run locally.
After the processes are up
- Open
http://localhost:8088/healthand confirm the server is healthy. - Open the admin portal and sign in through your issuer.
- Open Mailpit so you can see the next capture.
- Continue with First send.