Paste the one-time bootstrap token from the server logs
(or /data/bootstrap-token), then choose an admin password.
Data encryption key
One key decrypts this installation's message bodies. It is generated here, stored on this server outside the database's own volume, and printed on a sheet you keep — because a key that exists only on the server dies with the server.
Enter an existing key is for a reinstall onto a new server with the old database — that is what the printed sheet is for. Postpone leaves message bodies stored as plain text, which is a legitimate choice; the Settings page keeps saying so.
copied
The key is no longer on this page. Read the two groups off the sheet you just made.
Sign in
Break-glass sign-in
The coordinator's one local emergency account, for the day identity cannot sign anybody in. Every use is recorded in the audit trail and every administrator is told. The session lasts one hour and opens this console only.
Gateways
–
healthy / total
Active connections
–
client sessions
GEO zones
–
configured
TLS
–
Host load
–
1 min avg
Fleet
Every host and what is wrong with it. Counts of things that exist answer a question nobody has at 3am; this answers the one they do.
Host
Address
Role
Versions
Status
Reported
Note
Gateways
ID
GEO
Group
Address
Load
Status
Click a gateway for load & connection history.
Create GEO
Gateway groups
Egress classes for gateways. Mark those that reach the corporate network as corporate; then simply don't add them to guest/partner profiles' allowed gateways. A corporate group with a served whitelist below becomes a DMZ — its gateways forward ONLY these internal destinations and drop the rest (ADR-0006). Leave blank for an open corporate recorder.
Group
Type
Description
GEOs
Code
Name
Country
Group
Gateways
IPv6 marks a gateway whose host can egress IPv6. Clients capture IPv6 into the tunnel either way — leaving it outside was a leak — and drop it unless a gateway they selected is marked here, so a v6-only destination fails at once instead of stalling.
ID
GEO
Group
Address
Load
IPv6
Transports
Status
Actions
Gateway DNS (VLESS / Reality egress)
Resolvers gateways use for tunneled traffic. Without this, a gateway resolves via its host /etc/resolv.conf (often a broken VPS stub) — sites don't load though the tunnel is up. Applies on each gateway's next config pull; redeploy to apply now.
Rule lists
ID
Kind
Entries
Ver
New list
Profile
name
on
action
match by
value
→ GEO
Preview routing
Version history
Version
Default
Rules
Groups / roles → profile
Maps a user role/group to a routing profile and the gateway groups it may use. Corporate egress is separated simply by NOT ticking corporate gateway groups for guest/partner.
Group / role
Priority
Profile
Allowed gateway groups
Client tunnel profile
Remotely tune client transport knobs — fingerprint, block_quic, dns (mode/servers), fragment, mtu, mux, kill_switch (true/false — enforce blocking all traffic when the tunnel is down; omit to let users decide), reconnect (true/false — enforce auto-reconnect; omit for user choice) — without an app rebuild. JSON is overlaid global → group → user → device (most specific key wins); the client applies keys it understands and ignores the rest. Applies on the client's next connect.
Active sessions
User
Device
Gateway
Expires
Client sessions
Per-client monitoring — SW version, live per-location quality (traffic light), error count and online status. Aggregated from client telemetry + active sessions.
Remotely turn on verbose xray logging and log streaming for a specific device or employee — no app update needed. Takes effect on the client's next connect. Use the device id / username shown in Client diagnostics above.
Global (all clients): ·
Installed servers
What is actually running, one row per host, grouped by role — click a role to fold it. Hosts install their own software and update themselves; Settings changes what a host should be and reaches it on its next poll, and the ⋯ menu holds its log, the configuration it wrote, and decommissioning.
Host name
Address
GEO
Load / RAM / disk
TLS
Status
Actions
Coordination bus (Redis)
The bus the SFU pool, the MCU cluster, the recorder and the PSTN gateway coordinate over. It carries only state they rebuild within seconds — room routing, node records, device registrations, recording jobs — so it is tuned as a message bus, not as a database. Guide: the bus
Tuning
noeviction is the default for a reason: on a bus an evicted key is a room that silently vanishes from routing or a device registration a node stops forwarding; at the ceiling a write that fails loudly is the better failure. Persistence is off by default because nothing on the bus outlives its consumers: each one republishes its keys within seconds of reconnecting, and a snapshot restored after a restart would bring back rooms and registrations that no longer exist. Changes are applied to the running server — the bus is not restarted and keeps its data.
Password rotation
The bus first accepts both the old and the new password; then each consumer is moved to the new one in turn — it reads the password only at start, so it restarts, alone, and the next waits until it is back; only then does the bus drop the old password. No consumer is ever refused by the bus, but each one restarts once: calls on an SFU or MCU node reconnect when that node restarts, so rotate at a quiet hour.
Add a host
How to lay this out
One private network, and every rule is explicit.
All the hosts share it: postgres and the s3 object store, the services that use
them — messaging, calendar, identity, mail — and redis with the
sfu nodes, and nothing between them but rules this control plane writes. The database
accepts only the hosts that speak SQL — messaging, calendar, identity and mail — each as a
/32 in pg_hba; the object store only messaging, the recorder and mail, each as a /32 on
9000; the bus only the services that dial it — the SFU nodes, the SIP bridge, the
recorder — as a firewall rule on its own host. The plain-HTTP doors of identity, the calendar
and mail answer on their host's private address only. Every list is recomputed on each state
poll and applied within about a minute of a host appearing, so none of them is a rule somebody
has to remember to keep true, and none needs a redeploy.
Host
Public
Private
Why
postgres
—
yes
only the hosts that speak SQL connect — messaging, calendar, identity and mail — each allowed as a /32; nothing else is admitted, and nothing needs to reach it from outside
messaging
yes
yes
clients talk to it, it talks to the database
s3
—
yes
object storage for attachments, avatars, recordings and mail (ADR-0043): only the messaging, recorder and mail hosts connect, each allowed as a /32 on 9000. Optional for chat — without it attachments live on the messaging host's own disk, which means one host, one backup and no second messaging replica — and required for mail, which does not start without one. Give the host a second, EMPTY disk and name it when you add the host: the platform makes it a ZFS pool for the objects, which is what makes a snapshot the backup. The alternative to the role is an endpoint the organisation already runs, in Settings → External stores
redis
—
yes
the coordination bus; no client ever touches it, and 6379 is open only to the
services that dial it — the SFU nodes, the MCU, the PSTN gateway and the recorder. That rule is its whole boundary, so a host
with no ufw and no firewalld is refused rather than left reachable from the network
sfu ×2
yes
yes
media and signalling are public; the bus is reached privately
turn
yes
—
a relay talks to clients and to nothing of ours
gateway
yes
DMZ only, into the corporate network
see ADR-0006; a geo gateway needs no private leg
support
—
DMZ, beside the coordinator
the support bot's daemon (ADR-0031/0034): inbound only from the
messaging hosts (signed webhooks on :8087), outbound to messaging, to GitLab and to the model
provider; no client and nothing public ever reaches it. It holds its own provider key —
the bot's webhook points at it, and it calls the model itself — so there may be
several such hosts, one per case: different jobs, different knowledge bases,
different keys. Each is bound to its case when it is enrolled. It also keeps the bot's knowledge base:
it reads the product's repository and issues from GitLab (one archive request on first
sync, then only what changed) so the bot can answer instead of only filing bugs — so its
GitLab token needs read_api on that project, not just issue creation
directory
with a name
yes
the directory and the calendar as one binary (ADR-0054 §5): people, groups, sign-in and the platform's tokens (ADR-0047) on its listen port and its public name — browsers and phones open that name for the sign-in, the portal and its console — and the calendars, their entries, the ICS feeds and CalDAV (ADR-0038) on 8085 and the calendar's own name, which subscription links point at. One database on the same postgres (the calendar's tables in their own schema), its own login, created for it automatically; it obtains its certificates for both names
mail
443
yes
the mailboxes (ADR-0042): rows in postgres, bytes in the object store; clients read and send over JMAP on its public name; the mx hosts deliver into it over LMTP, privately
mx ×2
yes
yes
the mail edge: MX for the internet on 25, submission on 587/465, delivery out on 25; no database and no mailbox, only its queue. Two, in different failure domains
monitoring
—
yes
metrics, logs and traces of every host (ADR-0048): the collectors send to it privately, people open it by name on 443
mcu
LAN or public
yes
the SIP bridge for meeting-room systems (ADR-0007): reachable from the rooms, the bus privately
pstn
public (carriers)
yes
the PSTN gateway (ADR-0053): the carriers' SIP trunks, audio only; the bus privately. Never on an MCU host — both want 5060 and the same media range
recorder
—
yes
records meetings: it dials out to the bus, the object store and the SFU, and nothing dials it
The one rule that silently breaks calls.
A private address must never be advertised to clients. “Private address” goes in the
host’s private address field; the address clients use goes in Advertise IP for an
SFU. Put a private address in the second one and every call connects, carries no media and
times out with nothing in any log.
Order.
1) postgres → 2) messaging → 3) calendar → 4) redis → 5) sfu node A → 6) sfu node B → 7) turn.
Nothing needs redeploying afterwards: postgres re-reads its client list on every poll and admits
each new host’s /32 within about a minute. Mail adds 8) s3 → 9) mail → 10) mx ×2: mail does not
start without the object store, and identity goes before it when the people come from identity.
Calendar is optional and can wait; until a calendar host exists the clients hide their
calendar screens rather than offering buttons that fail, which is the honest answer for a
deployment that has not stood one up.
A second SFU node without redis is refused: two nodes without the bus each keep their own
room table, so two people “in the same room” on different nodes cannot see each other and
nothing reports a problem.
Sizing to start.
postgres 2 vCPU / 4 GB / 40 GB SSD ·
messaging 2 vCPU / 4 GB (attachments live on its own disk unless an object store is configured) ·
s3 2 vCPU / 4 GB and a second, empty disk sized from the media rather than from the people — about 1 TB per 1000 people per year of chat files, doubling if meetings are recorded routinely, and about 1.9 TB more per 1000 people per year once mail is deployed; it is disk-bound, and neither CPU nor network is the limit at that size ·
redis 1 vCPU / 1 GB (it holds room routing, not data) ·
each sfu 4 vCPU / 8 GB and real bandwidth — an SFU forwards every stream to
every participant, so the uplink is the limit long before CPU ·
turn 2 vCPU / 2 GB, bandwidth-bound for the same reason ·
recorder 4 vCPU / 4 GB per concurrent recording plus scratch disk — the one
media role limited by CPU rather than bandwidth, since it records by driving a headless
Chrome. It scales with recordings, not participants, and takes its jobs from the bus, so
one worker serves the whole pool; it needs a machine of its own, since a host carries one role ·
calendar 2 vCPU / 4 GB — it holds no files, and its whole working set is rows in
postgres; sized for the polling rather than for the people, since a subscribed Outlook and a
CalDAV client come back on their own schedule whether anybody is looking or not ·
support 1 vCPU / 2 GB / 10 GB — two small Go daemons plus the knowledge base,
which is held in memory (a repository the size of this one is ~6k chunks ≈ 100 MB with
embeddings, and its file on disk is the same order); searches are index-backed and answer in
under a millisecond, so the single core is the model provider's problem, not ours. The
thinking happens provider-side, so nothing here grows with chat load the way media hosts do.
Deploy it last: it needs messaging live and the support case’s provisioning (AI & automation → Support case) run, in either
order.
Ports the deploy opens for you (host firewall, when ufw
or firewalld is present): sfu 7881/tcp and 50000-60000/udp, plus 443/tcp once its certificate
exists — 7880/tcp only on a node with no public name, because a named node keeps signalling on
loopback behind its TLS front · turn 3478/udp+tcp and 49152-65535/udp, plus 443/tcp once its
certificate exists · mcu 5060/udp+tcp and 10000-20000/udp+tcp · pstn 5060/udp+tcp and its RTP range/udp (10000-20000 unless set), plus 5061/tcp once its certificate exists · recorder nothing at all ·
messaging its listen port, plus 443 when it terminates its own TLS · calendar 443 once it has a
public name, and its listen port (:8085 by default), answering on the private address only ·
identity its listen port (:8086 by default, on the private address only), 443 with a public name,
and 636/389 when its LDAP server is on · mail its listen port (:8087 by default, on the private
address only), 24/tcp to the mx hosts only, and 443 once it has a public name · mx 25/tcp, 587
and 465/tcp once it has a public name, and 2525/tcp to the mail nodes only · postgres 5432/tcp
to each SQL client’s /32 — messaging, calendar, identity, mail · monitoring 443/tcp, and 4319/tcp
to the hosts whose collectors send to it. And 80/tcp on every host whose certificate this
coordinator orders (sfu, turn, mx, monitoring), from its first order on: it stays open for
renewals, and nothing listens on it between orders.
Three ports are never opened to the world, and on a host with no firewall tooling the
deploy fails rather than leave them reachable: redis 6379, only to the addresses that dial the
bus — the SFU nodes, the MCU, the PSTN gateway, the recorder; s3 9000, only to messaging, the recorder and mail;
monitoring 4319, only to the hosts whose collectors send to it. With one private network that
rule is the only boundary each of them has, so it fails whatever address the service listens
on. postgres, mail’s 24 and the edge’s 2525 are narrowed the same way but do not fail
without a firewall: pg_hba, and the mail roles’ own check of who is calling, are then their only
boundary.
Certificates. messaging obtains its own (autocert) for its public name — the one given at enrolment or in its Settings. Calendar, identity and mail do the same, and for the same reason they are allowed to: they are our own Go services, so they run ACME themselves (TLS-ALPN on 443). An SFU cannot — that is
the whole reason edge exists — and the difference is worth knowing when a role
seems to be missing a certificate button. Subscription links are built from the calendar's
name, so it must already resolve to the host before the certificate can be issued. An SFU needs TLS in front of 7880 for iOS to connect at
all, and an enrolled host no longer needs you to arrange that: the coordinator runs the ACME order and the host serves the one challenge file on port 80, because the name points at the host while the account key stays here. So 80/tcp must reach the host from the internet — the agent opens it in the host's own firewall and leaves it open for renewals; a firewall or NAT in front of the host has to pass it too. From the moment an SFU has a name its signalling is on loopback, so it takes no calls until that certificate is on disk. The edge front then terminates 443 and
proxies every path to 7880 — every path, since moderation arrives on the same
host and port as the client’s WebSocket, and a front that forwards only the socket breaks
mute and remove while calls still look fine. The relay and the mail edge get theirs the same way — coturn reads the files on 443, mx presents its certificate for STARTTLS on 25 and on 587/465 — and a name ACME cannot validate takes an imported certificate (import on the row).
Add a host
The host joins by running one
command on itself (ADR-0025) — no SSH key here, no inbound reachability needed, so a machine
in a private network works the same as a public one. The invitation is single-use and expires
in an hour; the host exchanges it for its own credential and then pulls what its role needs. Guide: deployment
SSH key (only needed for "run it for me")
Used once and forgotten — it is never stored. Leave empty to use the coordinator's own key.
The token is not stored — only its
hash — so this command cannot be shown again. Generate another if it is lost.
Provisioning log
One row per host, newest attempt first; a +N marks how many earlier attempts that host had, whose history is in this row’s log. Hosts that pull their state show Settings instead of a redeploy, because there is nothing to push to them.
Role
ID
Host
GEO
Version
Status
Agent seen
CPU load
–
Memory
–
Disk
–
Uptime
–
Host logs
Asks a host for the last lines of one of the units it installed. Pulled on request rather than streamed, so an idle fleet costs nothing; the host caps the size and removes secrets before sending. The answer arrives on its next poll — usually within a minute.
pick a host and a unit
Coordinator logs
Monitoring
Metrics, logs and traces of every host, in Grafana on the monitoring host (ADR-0048). The console only lets you in: the button hands your browser a one-time pass, and the interface itself runs there.
Your own receiver
Send telemetry to a system you already run, over OTLP/HTTP (ADR-0048 §3a): Prometheus 3 takes metrics at /api/v1/otlp/v1/metrics (start it with --web.enable-otlp-receiver), Loki 3 takes logs, Tempo or Jaeger traces. Leave a field empty and that signal is not sent. With a monitoring host its gateway sends the copy; without one, every host with a private address sends directly.
Audit log
Time
Actor
Action
Detail
Users
Username
Number
Name
Email
Source
Roles
Groups
Invited by
Expires
Status
Actions
Rooms & devices
Bookable resources (ADR-0036 phase 5, ADR-0038 §1): a meeting room or a shared device is an account that never signs in, and it owns a calendar the same way a person does — which is what makes it bookable and what a free/busy grid reads. A panel is the screen on its wall: it authenticates to the calendar service with a credential of its own, polls that room's schedule, and holds no session — so a corridor full of panels keeps working through a coordinator outage. Issue one per screen and give it the wall's name: the label is what rotation replaces, so re-issuing under the same label kills the credential that screen was using. The address is shown once — only a hash is stored, and a lost panel is re-issued, never looked up. Guide: meeting rooms
Name
Login
Number
Kind
Seats
Location
Panels
SIP
Last call
Actions
Access recovery — pending callbacks
A guest said they cannot sign in. The person who INVITED them is asked first and issues the code from their own app — this card is the path for a guest whose sponsor is unavailable, or who was created by an administrator and so has none. Call the number yourself and dictate the code: it expires in 10 minutes and allows one password reset.
Guest
Phone
Code
Expires
Asked their sponsor more than fifteen minutes ago and still cannot get in. The sponsor may be away — issuing the code here does not wait for them.
Groups and their membership are identity's (ADR-0047 §8a): they are created, deleted and filled in identity's console. What stays here is the platform's access control — which roles a group's members get (user → groups → roles → permissions). A group's routing profile is in Routing → Groups.
Group
Kind
Roles
Actions
Calls & conferences (SFU)
Self-hosted the media server SFU for A/V calls, conferences, screen share. The coordinator mints room tokens (RBAC: calls:join / calls:host); media flows client↔SFU. Deploy the sfu and turn server roles, then enter the API key and secret here; the address clients dial is each SFU node's own public name. Guide: calls
The client URL is not a setting: each deployed SFU carries its own, and the
coordinator hands a client the one that serves its room. A single field could only ever name
one node, and it was the one place a wrong address could be typed — this one held the Redis
host for a while, which the panel reported and clients were sent to anyway. For an SFU inside
the perimeter, the address lives on the NODE and its subnet must be served by a gateway group,
or a phone has no route to it.
Numbering & routing (PSTN gateway)
Which trunk a dialled number leaves through, what is presented as the caller, and what is refused (ADR-0007 §C). Written here, stored as one versioned document, and pushed to the comms plane as a snapshot — never asked per call, because a control plane in the path of every telephone call is what service-roles.md §7 forbids. Guide: telephony
For the PSTN gateway, not the MCU. This plan governs the PSTN gateway — the separate role with carrier trunks (ADR-0053, Servers → role pstn). It is pushed to messaging, which authorises every telephone leg against it and signs the gateway's way into the room; the gateway itself holds no copy. Until dial-out ships (ADR-0007 phase 2) nothing places calls by this plan, and the stop switch already refuses at messaging's door. The MCU serves your meeting-room devices and routes no telephone number.
Two things here are the opposite of what you may expect, and both are deliberate. Order is declared twice. Rules are read top to bottom and the first one that matches wins — the rest are never looked at. The trunks inside a rule are tried in the order written. Teams randomises the gateways inside a route and documents that primary and backup therefore cannot be expressed at all; a cost difference between two trunks is the whole reason this feature exists, so ours is ordered. Unknown denies. An account with no office is a third state, not a default one: a rule locked to a site refuses that caller rather than treating them as head office. The fix is to give the account an office, not to loosen the rule.
Internal numbers
The range new people and rooms are numbered from, and the leading digits no person may have (ADR-0007 §2). The default is 2000–7999 with 8 for services, 9 for an outside line and 0 for the operator. An organisation migrating from Skype for Business may adopt its own plan here before its people arrive. A plan that would reserve a number somebody already holds is refused, with the holders named.
One control that stops outbound telephony for the whole organisation, audited, reachable by whoever is on call. It exists for the person looking at a graph at three in the morning who has to stop the bleeding before understanding it. Saving the tables below never lifts it.
Countries
The dialling context a number is read in, and the place one decision lives: outgoing dialling exists in a country only after somebody has answered for that country in writing. Where refusing an emergency call is not permitted, we do not offer outgoing telephony there at all — so this is a deployment answer, not a per-user permission, and an unanswered country simply has no dialler.
ISO
Code
Trunk pfx
Intl pfx
Number len
Dialling
Written answer
Sites
An office, as an administrative fact about an account — never derived from the network. This product ships a VPN client, so a client's network position is a statement about a tunnel exit and not about a person; Webex says the same from the other side. The main number is what somebody with no direct number of their own presents.
Id
Name
Country
Main number
Timezone
Trunks
The peers. Digits is a property of the peer, not of the number: normalise to +E.164 once, then hand each trunk the shape it asks for. Numbers is the registry of what may be presented on this trunk, one per line as +7495… | evidence — the carrier's assignment or the letter of authority. A carrier that catches you presenting anything else rewrites it at best and refuses at worst. Calibrated means this carrier's SIP codes have been measured: until they are, the interface shows five call states instead of nine, because a carrier that answers "nobody picked up" with 486 would make it say «Занято», and a person who reads «Занято» stops trying.
The second line of each trunk is the carrier on the wire. Carrier sources are the addresses its INVITEs come from: the PSTN gateway accepts a call only from a listed address (or, with challenge inbound, from one that answers the trunk's digest) and drops everything else without a reply. Outbound address, transport and port are where our calls go — TLS wherever the carrier supports it. The digest password is write-only: it is stored sealed and never shown again, a blank field keeps it, and it travels only to messaging, which answers the gateway's question about each call. REGISTER keeps a registration with a carrier that requires one.
Id
Name
Site
Digits
Prefix
Numbers
Max
State
Dial rules
Match is a regular expression over the normalised number and must be anchored — an unanchored pattern matching in the middle of a number is a bill nobody can explain, so it is refused when you save. From is which offices this rule is for, empty for any. Trunks are tried in the order written, comma separated. Caller ID is ddi, main, withheld or literal:+7495…. Lock means the rule may only use trunks belonging to the caller's own office and may not fail over out of it; hatch is the named exception that lets an account with no office use it anyway. Failover is allowed only before the callee's network has told us anything about the callee — once their phone has rung, another carrier only rings it a second time.
On
Id
Match
From
Trunks
Caller ID
Lock
Hatch
Failover
Press 1
Emergency numbers
A separate plane, not a rule. These are matched first, on the digits exactly as typed, before any normalisation — and they are refused, never routed. An emergency call is only useful if it reaches the service for the place the caller physically is, carrying an address a dispatcher can act on; we cannot establish that address, so a 112 dialled in one city and leaving through a trunk in another sends an ambulance to the wrong building. A half-working 112 is worse than an absent one. Emptying this table does not enable emergency calls — it lets those numbers fall through to ordinary routing, which is the one outcome this refuses.
Digits
Note
Spending limits
The attack this exists for is a revenue-share fraud: an attacker with a deal on a premium international range pushes as many minutes through your account as it will take, at night and at weekends, and the bill arrives a month later. Destinations are an allowlist by country code, never a blocklist — there are more premium ranges than anyone maintains — and an empty allowlist permits nothing. The counters live in the comms plane and are checked before any call is placed; the numbers are set here. A requested callback counts against all of this: it is an outgoing call at the organisation's expense to a number the caller chose, and a convenient option cannot be a way around a spend limit.
The spend is the sum of the call records' cost estimates, each from its trunk's tariff. A trunk without a tariff cannot be counted, so while a ceiling is set a call through it is refused — a ceiling blocks rather than hopes.
Test box
Type a number and an account, and see which rule matches, which trunks would be tried in which order, what would be presented as the caller, and the exact digits the first trunk would receive. Nothing is dialled. This exists because a dial plan is the one piece of configuration where trying it costs money and wakes a customer up — an administrator who has to place a real call to find out what their own rule does will not check, and the rule they did not check is the one that routes payroll's calls through the wrong country. Ask about an account rather than an office where you can: an account whose office was never set is exactly the fault this finds.
Inbound numbers (PSTN gateway)
What a telephone number reaches when somebody calls it from outside: a meeting, a person, or the conference number that asks for a meeting PIN. Stored here and pushed to messaging, which answers every incoming call from the PSTN gateway against it — never asked per call. Guide: telephony
A telephone caller is anonymous. The number they call from names their row in the meeting — masked unless you choose otherwise — and decides nothing: it never lets anybody in and is never matched to a colleague. By default a caller waits at the meeting's door, in the same waiting list as a guest, until a host lets them in; even when the meeting has no lobby, a telephone never starts a meeting alone.
Reaches: meeting — that meeting, while it is on, with no PIN (paste the meeting's room meet-… or its join link); PIN prompt — the conference number: the caller types the meeting's dial-in PIN and «#», three attempts; person — rings that person's apps like a colleague's call, and gives up after the ring time. A person in Do Not Disturb is not rung by a telephone.
main number — a greeting; the caller types a colleague's internal number (the extension in Users) or a meeting ID (the meeting's PIN), then «#». «0», or nothing at all, rings the operators — the logins in the third column, comma separated: one person, or up to ten rung at once, and the first to answer takes the call. Record name — before waiting at a meeting's door the caller says their name; the hosts can play it from the waiting list, and it is kept only while the caller waits. A person who misses a call from a telephone gets a message from the bot named above (in their chat with it), or, with no bot, a notice in the app.
Number
Reaches
Meeting room, person or operators
Record name
Note
Dial-in for a meeting
The numbers a meeting's telephone participants call and the meeting's PIN. The PIN is the meeting's own: nobody chooses it, and it opens the meeting only while the meeting is on.
Telephone call records (PSTN gateway)
Every telephone call placed from a conversation or a meeting, and every one the numbering plan or a limit refused — a burst of refused calls to one range is what fraud looks like before it works — and every call that came in on one of the organisation's numbers, with the trunk it arrived on. Kept by the comms plane, so they do not stop when this console is down. Answer rate falling on one route is a carrier problem before anyone reports it; an average call of a few seconds while the answer rate looks healthy is a carrier answering calls nobody answered. Both are counted over outgoing calls.
MCU: mixing & keypad (SIP endpoints)
A room telephone has one video window and no interface of ours in it — so which participant it shows, how long the picture waits before following a new speaker, and what its keypad does are the whole product on that device. Saved here, carried to the MCU with the desired state the host already polls, and applied without restarting it: an edit made while a room is in a meeting lands a minute later instead of ending the call. Guide: meeting rooms
default applies to every endpoint; by_device overrides it for one, keyed by the device's id in the registry of who may call (not the room's name, and not what the endpoint says about itself). Known settings: layout (speaker|grid), floor_ms, linger_ms, mix_voices, max_height, max_fps, content, allow_pin, allow_layout, keys. Keypad defaults: *1 mute, *2 hand, *3 pin, *4 layout, *0 help, ## leave — an empty value removes a key, and a key bound to an action the device is not allowed (allow_pin, allow_layout) is not bound at all rather than pressed and refused.
Out-of-range values are clamped, not refused: the MCU applies what it can and reports what it changed in its own health, because a room that mixes slightly differently is better than a room that does not answer. A key this build does not know is named there too — the one failure a settings file must never have is doing nothing in silence.
Calls right now
Every call on every MCU host, as the host itself last described it: which room is calling into which meeting, who is on its screen, who holds the content floor, and every sentence about what the call is doing differently from what was asked. It is the answer to «why does this room never see Ivan» without a packet capture. Refreshed every few seconds while this tab is open.
SIP SRV records
The DNS records room devices find the MCU nodes by (ADR-0053 §6.1). Configure each device with the SRV domain below as its SIP server, registration on and no password; it then picks a node by priority and weight and moves to the next one when a node does not answer or answers 503. The records are generated from the enrolled nodes' public names and caps; the coordinator manages no DNS, it checks what resolves.
Federation policy
Who this installation is on the wire, and which organizations it exchanges messages with. Federation is a contract between two administrators: each side lists the other, and a one-sided setup is refused by the other side — which is correct behaviour, not a misconfiguration. There are no shared secrets to exchange: each installation signs with its own key and publishes the public half, so parting company is a line removed from a list. Guide: federation
How the door is held. Today it is held by this list and nothing else — a message is accepted when the sending domain is on it, its signature verifies, and we have not seen that message before.
By this list
in force
By approval
a partner asks, you approve — designed, not built (it needs a pending state and an audited approve/deny, not a checkbox)
Open to anyone
refused until per-peer rate limits and an abuse rota exist — a flag that turns this into an open relay is a one-line change with a company-sized consequence
Offline depth
ADR-0020. How much history a client puts on the device at its FIRST sync, so it opens without a connection. The people list, the conversation list and the calendar list are synced whole and need no number — their size is bounded by the organization. Message history is not, so it is fetched by a stated depth: the newest N conversations, M messages each. Bigger means more works offline and a longer first sign-in; the clients cap it at 200 × 500 whatever is set here. Leave both blank and every client uses its own documented default (20 × 50) — which is a different thing from setting 0, and the storage screen on the device says which of the two it is. Applies at the next sign-in or fresh install; devices already warmed keep what they hold.
Bots
Non-human identities (ADR-0014/0021), authenticating to messaging with a scoped bot token. A service bot belongs to the tenant. A personal bot belongs to one user and is one of two things: an assistant — a session of that person, which reads what they read and writes in their name — or a worker, which acts under its own name and sees only the conversations it was added to. The owner is shown beside every personal bot, which is what makes free naming safe. Bots hold no admin role — a bot cannot sign in, so a role would be enforced nowhere; scopes and the conversation allowlist are what grant one anything. Rotating a token keeps the owner, the entity and the fence. Whether an assistant may act for its owner at all is a tenant-wide switch, in Settings → Acting on behalf of a user; with it off the tenant has no assistants, only workers.
Username
Name
Kind
Grants
Webhook
Status
Activity
Last seen
Created
Actions
Per user — who has an agent
Only personal bots are counted: a service bot belongs to the tenant, not to whoever created it. “Can act as them” is the row to read first — those are credentials that post in that person’s name.
User
Can act as them
Workers
Total
Stopped
Automations
Server-side rules (ADR-0014): a trigger (bot mention / keyword / schedule / webhook) runs linear steps; a step with an approve gate pauses until an allowed human reacts ✅ in the chat. Every rule speaks as a bot (as) and posts only into rooms that bot is a MEMBER of — so the member list answers "who can write here", and removing the bot from a room revokes it.
Name
Speaks as
Trigger
Steps
Enabled
Actions
Recent runs
Automation
State
Step
Updated
Error
Agent activity
Everything non-humans did — bot mutations, automation runs, approvals — merged from the coordinator and messaging audit trails.
Time
Actor
Action
Detail
Acting on behalf of a user
ADR-0021 §2. An assistant is a virtual session of one person — it reads everything they can read and posts in their name. This switch decides whether this organization may have them at all. The messaging service checks it on every request, not only when an assistant is created, so turning it off stops the ones already running rather than waiting for their tokens to expire. Worker bots — which act under their own names and see only the conversations they were added to — are not governed by it.
Loading…
AI lane (model provider)
The tenant's model-provider key behind the /ai/v1/* proxy (ADR-0030 §2). Bots never see it — each holds its own ai_… token, issued per bot in the Bots table with a model allowlist and a daily budget. Same rule as push keys: saving is enough, the messaging host's agent applies it within a minute. Check ai in the messaging /healthz. Guide: the AI lane
Personal quotas (token API). An assistant spends its owner's own quota at the gateway and never this tenant key — with no quota it is created without a brain, deliberately. Give the installation address and an Admin-role token, and each person's key is issued when they create their assistant and revoked with their account. The management routes live outside/v1.
Welcome flow
One button stands the whole thing up, idempotently (ADR-0023 Part 3): the Welcome Bot with a system badge, the welcome channel, the bot's membership in it, and the rule that greets whoever appears. Everything it creates stays an ordinary object afterwards — the bot on the Bots page, the room in the client, the rules in Automations — so an administrator edits the greeting there and re-running converges the rest around it.
The trigger is user_created, which the coordinator reports for an admin-created account and for a first SSO login. It greets people only — meeting rooms and bots are accounts too, and they used to be welcomed to the company alongside them. Membership is by invitation — the bot adds people as they appear — so anyone who joined the company before this was switched on is missing until you fill the room below.
The greeting is your text, not a product string: it lives in the rule and is never translated for you. Placeholders: {{display_name}}, {{user}}, {{company}}, {{department}}, {{title}}.
Support case (@librarian)
One button stands the whole ADR-0031 case up, idempotently: the bot (system badge, @librarian), its AI token, the «SimpleTwo Support» channel and «SimpleTwo Dev» room, the gated automation and the webhook secrets. Re-running converges rooms/automation and rotates both bot tokens — the support node picks them up on its next convergence. Then: enroll a support role host (Servers), add people to «SimpleTwo Dev», grant them the dev role. Requires the AI lane above.
There may be several such servers — different jobs, different knowledge bases, each with its own bot and its own provider key (ADR-0031 §5). Each is a case: pick one to edit it, or name a new one to stand another up. A support host is bound to its case when it is enrolled.
Names are per-install; blank keeps the current ones (defaults on first run). Rooms are tracked by id once created — room titles here only matter at first creation, and renaming a room later in the client is free.
This server's OWN provider credential (ADR-0031): the intermediary the bot's webhook points at calls the model itself, with a key limited upstream. Several such servers — different jobs, different knowledge bases — never share one. Leave the key blank to keep the stored one, or leave it unset to fall back to the platform lane.
Knowledge base (ADR-0034): the bot learns the product from GitLab — code, docs and issue history — so it can answer instead of only filing bugs. Blank fields keep the current settings; the embedding model must also be in the bot's AI allowlist, and needs an OpenAI-compatible provider (without one the base still searches lexically).
Mail DNS
The records each mail domain must publish for the edge (mx) to receive and send its mail: MX, SPF, DMARC, the DKIM keys this coordinator generated for the edge, and the submission and JMAP discovery records. Each is looked up; a record marked missing is one the world cannot see yet. Click a value to copy it.
Rotating a DKIM key
Three steps, and a new domain's first pair takes the same two. New DKIM key makes a new pair: publish its two records. Activate the new key becomes available once DNS answers with them; the edge then signs with the new pair and the old one is retired. Keep the retired records published until the date shown: mail signed with them is still being verified. The private keys are sealed with the master key when encryption at rest is on.
Service addresses
The addresses every mail domain must answer at: postmaster@ (required by RFC 5321), abuse@ (complaints about mail from here), and dmarc@ and tls-reports@, where the domain's DNS records ask others to send their reports. Missing ones are made as identity groups that cannot be deleted by accident and that anyone may write to, with you as the first member; add the people who answer them in Users → Groups.
Address
What it is for
Held by
Mail devices
Machines on the internal network that cannot sign in with a password — scanners, an ERP, monitoring. They connect to the mail edge (mx) on port 25 from the networks listed here and may write only as the senders listed, in this installation's mail domains. Everything else stays with app passwords. The internal network must be able to reach the edge's port 25.
Name
Networks
Senders
Recipients
Per minute
Max size, MB
TLS
Networks and senders: several, separated by commas. A sender is an address or a whole domain as @corp.example. The narrowest network decides which connector a machine belongs to.
Edge protection
What the mail edge (mx) checks on mail from the internet, and where it hands mail out. The content filters are the organisation's own services — rspamd or clamav-milter over milter, an anti-virus over ICAP — asked in this order; the edge runs none itself. Every mx node takes these settings on its next poll and restarts with them. An empty field keeps the edge's default.
Name
Protocol
Address
If it does not answer
A virus
Virus headers (ICAP)
Timeout
Address: inet:10.0.0.5:11332 or unix:/run/rspamd/milter.sock for a milter, icap://av.corp.example:1344/respmod for ICAP. A filter that does not answer lets mail through unchecked (open) or keeps it waiting at the senders (closed). Virus headers: the headers your anti-virus answers with when it finds something; empty is the common products' own.
587 and 465: empty or 0 is no bound, the default. A bound per address counts everyone behind one NAT as one client (an IPv6 network by its /64).
Block and allow lists: zones separated by commas, zone=127.0.0.2|127.0.0.4 to count only those answers, off for none; empty is zen.spamhaus.org and no allow list. Connections: 0 is no bound. The relay: empty delivers to each recipient's MX directly — set it when the provider blocks outbound port 25; several, separated by commas, are tried in turn. A trusted ARC sealer — a domain, with its subdomains — lets a message whose DMARC failed through when its chain passes and that sealer found DMARC passing; with none, chains are recorded and decide nothing.
Quarantine
Mail the anti-virus or a content filter held on the way in. The sender was told it was delivered; each recipient got a notice from the postmaster instead of the message. Only here can it be released to its recipients or deleted; what nobody decides on is deleted after 30 days (MAIL_QUARANTINE_DAYS).
Held at
Sender
Recipients
Subject
Held because
Message size
Group members outside
Partners and other outside addresses in groups whose mail comes back. After five bounces in a row, each within a week of the last, a member is disabled: its copies stop until you enable it again here — do that once the address works again.
Group
Member
Bounces
Last bounce
State
What comes back from a group's members outside is kept in the mailbox named here for the group, and that mailbox gets a letter when a member is disabled. With none named, or while that mailbox is archived or belongs to a person who has left, the postmaster of the group's domain gets them.
Mailboxes
Every mailbox the mail role holds: people's, made from the directory; shared ones, made here; rooms and resources. A shared mailbox has a responsible person and the people or groups who may open it, each with what they may do and whether they may send as it. A legal hold stops retention and any cleanup from destroying the mailbox's mail, whoever it belongs to.
Address
Display name
Kind
Responsible person
Mailbox size
Mailbox state
Legal hold
Mail goes to
New shared mailbox
Sending as a group
Who may write to a group is identity's rule (the group's writers). Here is who may send AS the group, from the group's own address: as — the message is the group's alone; on behalf — it names the person in Sender. Every message sent as a group is written to the access audit.
Group
Address
May write to it
May send as it
Message trace
The path of a message, from the edge that took it to a mailbox or a remote server: search by an address, or by the queue id either side gave (a bounce and the sender's server name it). Who wrote to whom is personal data, so this card needs the audit:read permission.
What the submission doors (587 and 465) refused is listed under the path — a device or a program that cannot send: search by its login or sender address, or by its IP address or network. Repeated refusals are counted, not listed one by one. A wrong app password or a locked account is also in identity's sign-in records, with the client's address.
Time
Event
Where it happened
Sender
Recipient
Next hop
Server reply
Details of the event
Refused on the submission doors
Time
Count
Door
Client
Login and sender
Step
Server reply
Why
Mailbox access audit
Who opened which mailbox and did what: access to shared mailboxes and through delegation, and always what changes who can reach a mailbox or where its mail goes — a recall, an import or export, sending as somebody, an app password, a release from quarantine. It holds no subject and no body. Needs the audit:read permission; the export is JSON lines for a SIEM.
Time
Who acted
Mailbox
Action
Client and method
Details of the access
Disclaimer
A text added to every message a person sends from the domain, to every recipient, before the edge signs it. The marker is how a reply that already carries it is recognised, so it is not added twice.
Leavers' mailboxes
A person's mailbox outlives their account: kept indefinitely unless a policy is set. With one, it is archived — accepts no mail and stays readable — or purged after the days below, and the responsible person is told before. A mailbox under legal hold is never purged; one made shared is the organisation's and is left alone. To find them, filter Mailboxes by "left by a person who has gone".
Deletion and recovery
How long deleted mail lives. Trash and Junk are emptied after their days. What is deleted — from there, by a person, by a recall or by the leavers' purge — leaves every device and waits out the recovery window whole, so that you can bring it back; then it is erased for good, and its bytes a day later. A mailbox under legal hold erases nothing, and its people can still delete. Guide: mail
0 for Trash or Junk: never emptied. 0 for the window: deleted mail is erased within the hour.
A mailbox's deleted mail
Deleted on
Sent by
Subject
Received on
Erased after
Forwarding outside
Whether a person's rule may forward a copy of their mail to an address outside the organisation. Off, such a rule forwards nothing outside — the way most organisations keep it, because a forwarding rule is how mail leaks. On, the copy leaves with its envelope rewritten (SRS), so that a bounce finds its way back to the sender, and sealed with ARC; mail the filters judged junk is never forwarded. The list shows every mailbox whose rules forward outside, whether or not it is on. Guide: mail
Mailbox
Forwards to
DMARC reports sent
The aggregate reports sent to the domains that ask for them, one a day per domain, for the last thirty days — or why one went to nobody.
Day
Domain
Messages counted
Records
Sent to
Not sent because
TLS reports sent
The TLS reports (RFC 8460) sent to the domains that ask for them, one a day per domain, for the last thirty days: how many sessions to their mail servers negotiated TLS and how many failed — or why a report went to nobody. A domain asks with its _smtp._tls record; reports go to mailto: addresses only.
Day
Domain
Sessions with TLS
Failed
Sent to
Not sent because
What the drive holds
Charged against stored is the number worth watching: the drive stores the same file once however many people keep it, and the gap between the two is what that is saving you. A space whose quota is spent stops accepting new files and goes on serving the ones it has.
Where the space went
The files people see, the bin and the earlier versions are three different costs, and only the first shows in anybody's file list. The largest files are the quickest to free: a file in the bin goes by itself when its days are up.
Space
File
File size
Added by
Spaces
A person's own space is made for them when they are given the drive; a shared space is made on purpose — here, or in the app by somebody with the right to — and is opened to groups and people at a level. A quota of 0 means the installation's default applies; setting one below what is already stored refuses new files and takes nothing away.
Space
Belongs to
Used
Quota, GB
Files of people who left
A person who leaves keeps their files here, read-only for everybody, under the same rules as their mailbox: the retention policy, the responsible person and the legal hold are set on the mailbox (Mail, Mailboxes), and the files follow it. Making the mailbox a shared one keeps the files too, managed by its responsible person.
Person
Left
Used
State
Client apps
What the Dashboard and the sign-in screen offer people as a way to install SimpleTwo. Store links go to TestFlight / Google Play; the rest are builds hosted here, in the coordinator's data volume. Both the list and the downloads are public — the person who needs a client cannot sign in from one yet. Guide: client apps
Platform
Published
The mac-v* and android-v* pipelines POST the build they just signed to <public-url>/v1/clients/<platform> with this token. It authorises that one thing and nothing else — not an admin session, which is what a pipeline holding admin credentials would be. Rotating stops the pipelines publishing until the CI variable is updated.
Encryption at rest
ADR-0039. One master key, kept in a file on this server outside the database's volume, and printed on a recovery sheet with a QR code. The working keys — message bodies, the blind search index, meeting transcripts — are derived from it, so the safe holds one sheet rather than one per purpose. Generating, rotating and re-printing need the encryption:keys permission and leave a line in the audit log; reading a body in the clear needs messages:export. Neither is in any built-in role.
Creating the key, changing it and printing the sheet again all need the encryption:keys permission, and your role does not hold it — no built-in role except owner does. Ask an owner, or add that permission to your role in Users → Roles.
New generation re-derives the working keys from the same master: new writes use them, old rows keep opening, and the printed sheet stays valid — this is the answer to a key that may have leaked from a messaging host. New master key prints a new sheet, and rows already sealed keep needing the old one.
Product options
The core — the coordinator, Chats and the directory with the calendar — is always on; everything else is an option: switch it per tenant and, where it applies, choose HOW it is reached — directly over TLS (works without the VPN) or through the tunnel only. Clients hide disabled modules. Secure Access is the VPN itself — the gateways and the tunnel; off, no gateway is enrolled and every mode here reads direct. Audio/Video has a tier: Basic is calls, conferences and recording; Plus adds rooms (the MCU), telephony with PSTN, call recording, federation, transcripts and the AI support. Skills (peer-confirmed skills, endorsements, kudos) is served by messaging and follows its access mode; it ships OFF, because collecting this about people is a decision an organization makes rather than one it discovers. Federation lets people here write to name@another-domain and is the one switch that lets messages leave this organization — it ships OFF, needs the policy below and Audio/Video Plus. ID is identity as the provider; off, identity is the directory alone. Greyed rows are designed and not built.
Module
Enabled
Access
Secure Access
the gateways and the tunnel; off, every mode below reads direct
Messaging
Audio/Video
Skills
follows Messaging
Federation
server to server, over public HTTPS
Work
SimpleOne tasks on the device, offline — designed, not built
ID
identity as the provider; off, identity is the directory alone and sign-in is the company provider's
Drive
the tab appears once a drive host answers on its public name
Mail
General
Camouflage each gateway under a country-plausible host (RU→yandex.ru, NL→a .nl site). Verify on the gateway with xray tls ping <host> — pick one showing Post-Quantum: false (X25519). Per-gateway blank = inherit global. Applies after the gateway's next Update.
XTLS-Vision. Splices the inner TLS so a DPI cannot see TLS-inside-TLS — the fingerprint plain VLESS+Reality carries, and the one a path that kills established flows keys on. It is the designed answer to "the tunnel connects and passes nothing". The risk is the mirror image: the mobile (libXray) build has been measured negotiating Vision and never relaying the RESPONSE — gateway acknowledges, fetches, and sends a FIN with zero data. One switch for the whole install, because both ends must agree: gateways re-render within a heartbeat, clients pick it up on their next session. Turn it off here the moment mobile goes quiet.
External Postgres and Redis
Use stores the organisation already runs instead of the ones the postgres, redis and s3 roles install. Leave a field empty and that store keeps coming from its role — this is a switch, not a migration: nothing is copied, so point a service at a new database only when it is empty or already holds its data. Guide: storage
Give each service a connection string its own login can use. We do not create databases or roles here: a cluster you do not own will not grant that, and half-creating something on it is worse than asking.
Checks dial from the coordinator. The host running the role still needs its own route to the database — that is the one thing this button cannot see. Saved values reach a role on its next poll, and the service restarts with them.
Push notifications (APNs)
One key, both kinds of push. This is not calls-only: the same APNs key rings incoming calls and delivers every message notification. Leave it blank and users get no message banners at all — the messaging service builds no APNs client and every notification silently becomes a no-op. Saving is enough: the key is part of the messaging host’s desired state, so its agent picks the change up on its next poll (about a minute) and restarts the service. Nothing to redeploy — a host that pulls has no redeploy.
Leave OFF for TestFlight and App Store builds — they are signed aps-environment: production, and the sandbox host rejects their tokens as BadDeviceToken. Only a Xcode-built debug install needs this.
Push notifications (Android / FCM)
The Android half of the same story: without it Android devices get notifications only while the app is open. Same rule as APNs — saving is enough, the messaging host’s agent applies it within a minute and restarts the service. Check push_android in the messaging /healthz to see it land.
Firebase console → Project settings → Service accounts → Generate new private key. This is notgoogle-services.json: that one is the client config, it belongs in the Android build (CI variable ANDROID_GOOGLE_SERVICES_JSON), and it is rejected here.
Identity
The organisation's directory and the platform's sign-in (ADR-0047 §8a): people, groups and their membership, the attribute schema, address books, applications, company providers, SCIM, the LDAP server and the sign-in policy are identity's, and are managed in its own console (the link above). This card is the role's status, like every other role's. Guide: groups and the schema
Break-glass account
One local account on the coordinator for the day identity is down and the fleet — identity included — has to be repaired (ADR-0047 §8a). A password and a mandatory authenticator code, held here and never on identity. It signs in only at its own address, every use is audited and every administrator is messaged, and its session opens this console for an hour. Keep the password and the authenticator somewhere the on-call administrator can reach without the platform.
Vendor support access
Read-only access to this console's API for the vendor's support engineers — SimpleOne VCSM first — so a case can be diagnosed without an account here (ADR-0052). A token reads what a viewer reads, except every screen that holds a secret, and changes nothing. Only an owner issues one, for one to three years; the secret is shown once; every issue, revocation and use is in the audit log. Guide: vendor support access
Sign-in through identity
Four switches. Platform token: who signs people in — this coordinator (hmac, today) or identity, whose token every service then accepts alone. Service token: what the services sign their calls to each other with — the shared secret (hmac) or each host's own key. CalDAV and CardDAV: what a calendar or contacts program signs in with — the calendar's device passwords (calendar, today) or identity's app passwords alone. Messaging directory: where chat takes people and groups from — this coordinator's directory events (coordinator, today) or identity's directory feed, which shows chat only the people provisioned for it. Each switch refuses to move forward while anything below stands in its way; switching back is always allowed. Runbook: switching to identity
Platform token
Service token
CalDAV and CardDAV
Messaging directory
Migration tool: svc:move stopgap
The migration tool moves data with a wave's credential, which identity issues and which reaches only the wave's people. The stopgap is the old way in: a plain svc:move service token signed with the shared secret, which names no wave and so reaches every person's books on identity and the calendar. It is shut. An owner may open it for at most 72 hours, with a reason; identity and the calendar close it by their own clocks at that instant. Every use is audited on the role and counted in its /healthz, and every administrator is told when it opens or closes.
Warning: while it is open, anyone holding the shared secret can read and write every person's address books. Open it only for a migration run that cannot use a wave's credential, for as short a time as the run needs, and close it as soon as the run ends.
Local time; at most 72 hours ahead.
Required, for opening and for closing. It is in the audit and in the administrators' message.
Directory photos (AD / LDAP)
This card feeds identity's directory (ADR-0047 §8a). Its passes run on the coordinator only until identity takes them: as soon as an identity host is deployed, the coordinator hands it the connection, the bind password, the watermarks and the last results once, and deletes its own copies. Without identity they stay here. The photo policy below stays here either way.
Employee avatars are pulled from AD (thumbnailPhoto) after each SSO login and pushed to messaging. Guests/external accounts upload their own photo in the app.
Groups (ADR-0047): with a group base DN the directory's groups — objects, their manager as owner, every member — are written into the identity role every six hours and on demand, so a group exists before anybody in it has signed in. Same groups the sign-in already writes; this pass says all of it at once. Members are matched to accounts here by userPrincipalName, then mail, then sAMAccountName; the unmatched are counted, never guessed.
People (ADR-0047 §7a): every six hours and on demand, each account here takes from the directory the attributes the identity schema maps with source: ldap and an ldap_name — nothing else is read — and its manager, resolved to an account here. Standing (the disabled bit, accountExpires) is counted on every pass and applied only with the switch below: it is the one part that can lock a person out.
Passwords (ADR-0047 §7a): identity holds a copy of this connection for the bind pass-through. Whether it binds at all is identity's own setting, switched in identity's console.
SimpleOne (HRMS)
This card feeds identity's directory (ADR-0047 §8a). The HRMS module runs on the coordinator only until identity takes it: as soon as an identity host is deployed, the coordinator hands it the connection, its secret, the settings and the last run once, and deletes its own copies. Without identity it stays here.
One connection to a SimpleOne instance, shared by every module integration; HRMS is the first. It enriches employee cards with the two managers an organization actually has — functional and organizational — which AD cannot express, since it carries a single manager. Plain directory data: nothing here is behind the Skills switch. We always call them, never the reverse (their Scripted REST executes as Guest User on a bad token).
The platform's own way to read a table is the Table API: /rest/v1/table/employee, which answers {"status":"OK","data":[…]} and takes Basic or Bearer. A path shaped like /v1/api/… is a Scripted REST action, which exists only if somebody built one on your instance.
Four things the query has to do, because nothing here can do them for you:
narrow it to real staff (on SimpleOne: sysparm_query=active=1^company.class=internal, or contractors and closed accounts arrive too),
ask for display values (sysparm_display_value=1) — a manager without one comes back as an 18-digit record id, and the report counts those rather than writing them,
walk the reference where the field you want belongs to another record (immediate_unit_id.manager is the manager of the person’s own unit, and timezone_id.name is the zone name a calendar can load rather than the “(GMT+03:00) …” label),
and raise the page size (sysparm_limit=1000) — the default is twenty. Pages after the first are read automatically; pin sysparm_page yourself only to look at one.
The person's working hours in the calendar — the frame a scheduling wizard proposes meetings inside, and until now the tenant's single answer for everybody. work_schedule on the employee record is a reference to a schedule, so what arrives is its name; the hours live in that schedule's elements, in a shape (night shifts, holidays, two levels of inclusion) that days/start/end cannot hold. So state the translation here once. Days are ISO weekdays, 1 = Monday. Run the preview first: it lists every schedule name your instance actually answers with, and how many people are on each. A name you leave unmapped is reported, not guessed at — those people keep the hours they chose. A name you map takes the field over: their calendar shows it as coming from HR and refuses edits, on every client.
Domains
Every domain of the organisation, and what each service does with it. Sign-in: people may sign in with an address in it (Single sign-on, Served mail domains; identity takes this list over). Mail: the mail role accepts mail for it — mailboxes (its addresses are mailboxes, aliases and groups here; a primary address may only be in such a domain), aliases only (receives for aliases of mailboxes in another domain), or not at all. The primary mail domain is where postmaster@, dmarc@ and no-reply@ are. A domain that still holds addresses cannot be removed from mail, and the mail role says why.
Domain
Sign-in
Mail
Holds
Quotas
A template says how much a person may keep, and the largest thing they may put, in each service: mail (the mailbox, and the largest message they send), chats (every file they uploaded to chats, and the largest file) and files (their drive space, and the largest file). Empty is no limit. A person gets the template given to them here; otherwise, in each service on its own, the most generous of their groups' templates; otherwise the organisation's. Over a quota, new mail to the mailbox and new uploads are refused; reading never is, and nothing is deleted to make room. Guide: quotas
Quota template
Mailbox, GB
Largest message, MB
Files in chats, GB
Largest chat file, MB
Drive space, GB
Largest drive file, MB
Templates given to groups and people
Group or person
Their template
What one person gets
In the service
Their limits
Comes from
In use
TLS / Certificate
Let's Encrypt (auto)
The address Let's Encrypt writes to when a certificate is about to expire and renewal has not happened. Issuance works without it — and then nobody is warned.
Point these hostnames (DNS) at this coordinator. Once Let's Encrypt is enabled above, a certificate for each name is issued on first connect. The mobile app discovers the coordinator via https://<host>/.well-known/simpletwo.json.
Let's Encrypt is off — these names are served with the self-signed certificate and no certificate is being issued. Fill Domain above and press Enable Let's Encrypt (contact email is optional).
Password autofill on iOS and macOS
A saved password belongs to the domain it was saved on, and Apple will not hand one to an app the domain has not vouched for. The vouching is /.well-known/apple-app-site-association, and this coordinator already serves it on every name that reaches it — so every domain listed above is covered with nothing to host.
The other half is in the APP, and it is baked at build time: the client must claim each of these domains. If autofill offers nothing on one of them, that is the half that is missing — not this list. The lines the app build needs:
webcredentials:<each domain above>
Apple fetches the file through its own CDN and caches both success and failure for up to a day, so a freshly added domain is not offered immediately. sudo swcutil developer-mode -e 1 on a Mac makes it fetch directly and is the way to check before the cache turns over.
How auto-discovery works & setup
The mobile app asks users only for email + password and finds this coordinator from the email domain. This coordinator: —. It tries, in order:
Subdomain (recommended) — create a DNS record s2.<yourdomain> pointing here (CNAME to this coordinator's host, or an A record to its IP), then add s2.<yourdomain> to Tenant domains above, with Let's Encrypt enabled. The coordinator serves the discovery document and issues a certificate for that name on first connect. Nothing else to host.
DNS TXT — add _simpletwo.<yourdomain> TXT = simpletwo=https://<coordinator> (used only if 1–2 fail; may be blocked on some networks).
Example for user@itglobal.com: DNS s2.itglobal.com → this coordinator, add s2.itglobal.com to Tenant domains. The app then discovers it via https://s2.itglobal.com/.well-known/simpletwo.json. Verify anytime by opening that URL in a browser.