Meridian handbook
ReferenceConfiguration

engine.yaml: authentication and the MCP

Every key of engine.yaml's auth section (local staff accounts, OpenID Connect single sign-on, staff and member sessions, member sign-in) and its mcp section (the remote content MCP endpoint, its clients, tokens and limits), with type, default and meaning.

Staff and members are two separate realms with their own sign-in and sessions. Staff sign in to the admin with a local account (password plus a second factor) or through your identity provider over OpenID Connect; members (engine-dmo) sign in to the portal with a magic link or a passkey. The mcp section switches on the content MCP endpoint that lets a person connect an AI agent to the admin.

auth.staff.local

auth:
  staff:
    local:
      enabled: true
      require_mfa: true
      breach_check: local
KeyTypeDefaultMeaning
auth.staff.local.enabledbooleantrueStaff may sign in with an email address and password
auth.staff.local.allowliststringEmptyComma-separated email addresses. When set, only these may sign in with a local account
auth.staff.local.require_mfabooleantrueEvery local account must enrol a second factor. false is refused in production
auth.staff.local.password_min_lengthinteger12The shortest password accepted. At least 12
auth.staff.local.breach_checkstringlocallocal checks a new password against the breached-password filter shipped in the binary, with no network request; hibp checks that, then the Have I Been Pwned range API; off checks nothing (a warning in production)
auth.staff.local.hibp_urlstringEmptyThe range endpoint for hibp, to which the SHA-1 prefix is appended, such as a mirror behind an egress proxy. Empty is the public service. https (or http outside production)
auth.staff.local.argon2.memory_kbinteger65536Argon2id memory per hash, in KiB. At least 19456
auth.staff.local.argon2.timeinteger3Argon2id iterations. At least 2
auth.staff.local.argon2.threadsinteger2Argon2id parallelism. At least 1

engine-dmo doctor reports the measured time of one password hash with these parameters.

auth.staff.oidc

Single sign-on through your identity provider (Microsoft Entra ID, Google Workspace, Okta, Keycloak, Auth0 and any other OpenID Connect provider). A user’s groups in the provider map to Meridian staff groups.

auth:
  staff:
    oidc:
      enabled: true
      issuer: https://login.microsoftonline.com/<tenant id>/v2.0
      client_id: ${OIDC_CLIENT_ID}
      client_secret: ${OIDC_CLIENT_SECRET}
      group_claim: groups
      group_map:
        "a1b2c3d4-web-editors": editors
        "e5f6a7b8-marketing": [editors, analysts]
KeyTypeDefaultMeaning
auth.staff.oidc.enabledbooleanfalseOffer sign-in through the provider. Needs issuer and client_id
auth.staff.oidc.issuerstringEmptyThe provider’s issuer URL; its discovery document is read from it
auth.staff.oidc.client_idstringEmptyThe client registered with the provider
auth.staff.oidc.client_secretsecretEmptyIts secret
auth.staff.oidc.scopeslist of strings[openid, profile, email]The scopes requested
auth.staff.oidc.group_claimstringgroupsThe ID token claim that lists the user’s groups
auth.staff.oidc.group_mapmap{}Provider group value to one Meridian group key, or a list of keys
auth.staff.oidc.unmatched_groupsstringignoreA provider group not in group_map: ignore or warn (logged)
auth.staff.oidc.no_mapped_groupstringrefuseA user none of whose groups is mapped: refuse the sign-in, or provision them with provision.groups alone
auth.staff.oidc.auto_provisionbooleantrueCreate a staff user on first sign-in
auth.staff.oidc.link_existing_by_emailstringverified_onlyLink a first sign-in to an existing staff user with the same email: verified_only when the provider says the email is verified, or never
auth.staff.oidc.provision.display_name_claimstringnameThe claim a new user’s display name is read from
auth.staff.oidc.provision.locale_claimstringlocaleThe claim their admin language is read from
auth.staff.oidc.provision.groupslist of strings[]Groups every provisioned user joins
auth.staff.oidc.deprovision_onstringabsentGroup memberships always follow the provider at each sign-in, and a change ends the user’s other sessions. absent: a user who signs in with no mapped group at all, and holds no admin membership, is suspended (a later sign-in with a mapped group restores them); never: not suspended
auth.staff.oidc.required_acrstringEmptyAn authentication context class the provider must assert (sent as acr_values and checked in the token), to require the provider’s own MFA

auth.staff.session

KeyTypeDefaultMeaning
auth.staff.session.absolute_lifetimeduration12hThe longest a staff session lives
auth.staff.session.idle_timeoutduration2hA session unused for this long ends. Positive and not longer than absolute_lifetime

auth.member

Members are the people behind partner organisations, signing in to the member portal (engine-dmo).

KeyTypeDefaultMeaning
auth.member.enabledbooleantrueMembers may sign in
auth.member.magic_link.enabledbooleantrueSign in with a single-use link sent by email
auth.member.magic_link.ttlduration15mHow long a link works
auth.member.passkeys.enabledbooleantrueMembers may add and use passkeys
auth.member.password.enabledbooleanfalseMember passwords are not built; true is refused
auth.member.registrationstringclaimHow a member relationship starts: claim (staff invitations and the public claim form) or invite_only (staff invitations alone). open is refused
auth.member.session.absolute_lifetimeduration720hThe longest a member session lives (30 days)
auth.member.session.idle_timeoutduration336hA member session unused for this long ends (14 days)

mcp

The content MCP endpoint lets a staff member connect an AI agent (a Model Context Protocol client) to the admin, acting as themselves with their own permissions, after a consent screen. It is off unless mcp.enabled is true; off, there is no endpoint, no metadata and no admin screen for it. It is served on the admin’s origin at <admin_path>/mcp. engine-dmo mcp over standard input and output, for a local agent in development, does not read this section except personal_data.

mcp:
  enabled: true
  personal_data: refuse
  clients:
    - id: acme-agents
      name: Acme agent platform
      redirect_uris: ["https://agents.acme.example/callback"]
KeyTypeDefaultMeaning
mcp.enabledbooleanfalseServe the endpoint and its OAuth authorisation server
mcp.registration.metadata_documentsbooleantrueAccept clients that identify themselves by a client ID metadata document (a URL client_id)
mcp.registration.dynamicbooleanfalseAccept dynamic client registration
mcp.clientslist[]Pre-registered public clients using PKCE
mcp.clients[].idstringRequiredThe client_id the client sends, such as acme-agents. Not a URL; unique
mcp.clients[].namestringRequiredThe name the consent screen shows
mcp.clients[].redirect_urislist of stringsRequiredhttps URLs, or http on a loopback address, with no fragment
mcp.tokens.access_ttlduration1hAn access token’s life, 5m to 24h. Every call looks the token up, so revocation is immediate
mcp.tokens.refresh_idleretention30dHow long a refresh token may go unused, 1d to 180d
mcp.tokens.connection_maxretention90dThe longest a connection or a personal access token lives, 1d to 180d
mcp.personal_datastringrefuserefuse keeps CRM personal data away from agents; allow lets a person choose the toolsets that return it on the consent screen
mcp.media.fetchbooleantrueAllow uploading media by URL; off refuses every upload by URL
mcp.media.fetch_hostslist of strings[]When set, only these hosts (and their subdomains) may be fetched from
mcp.limits.calls_per_minuteinteger600A connection’s weighted calls a minute (a read weighs 1, a search 2, a content check 3, a write 5, a bulk plan 1 an item), counted per replica. At most 1200
mcp.limits.writes_per_minuteinteger60A person’s writes a minute across all their connections. At most 120
mcp.limits.writes_per_dayinteger2000A person’s writes a day. At most 5000
mcp.limits.bulk_itemsinteger200The most operations one bulk plan carries. At most 500
mcp.limits.media_by_url_per_hourinteger20A connection’s uploads by URL an hour. At most 100

A limit left out (or 0) takes its default. See The content MCP.