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.

SettingEnvironment variableWhat it does
Mursil:MultiTenancyMursil__MultiTenancyDeployment tenancy switch. Default false.
Identity:AuthorityIdentity__AuthorityOIDC issuer. Required for a published deployment.
Identity:AudienceIdentity__AudienceExpected API audience. The local default is mursil-api.
Identity:ClientIdIdentity__ClientIdAdmin portal client id. The local default is mursil-admin-panel.
Identity:ClientSecretIdentity__ClientSecretAdmin portal client secret. Set it from your secret store.
Email:Smtp:HostEmail__Smtp__HostSMTP host. Local compose points this at Mailpit.
Email:Smtp:PortEmail__Smtp__PortSMTP port.
Email:Smtp:EnableSslEmail__Smtp__EnableSslWhether 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:

RoleWhat it can open
SystemAdminEvery portal page, including the full menu.
TenantOwnerOperator pages, templates, branding, and owner-only actions such as creating a provider connection.
TenantAdminOperator pages, templates, and branding.
TemplateEditorTemplates and branding.
OperatorDashboard, messages, failures, audit, providers, sender domains, and settings.
ViewerDashboard, 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.

SurfaceAddress
Server healthhttp://localhost:8088/health
Adminhttps://localhost:8089/
Development issuerhttps://dev-issuer:8443/
Mailpithttp://localhost:18025/
Mailpit SMTP from the hostlocalhost:10259
Postgreslocalhost:54329
Redislocalhost: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

  1. Open http://localhost:8088/health and confirm the server is healthy.
  2. Open the admin portal and sign in through your issuer.
  3. Open Mailpit so you can see the next capture.
  4. Continue with First send.