Roles at GuestGet#

GuestGet is the company; everything else on this host is legacy that will go. This page is the role catalogue: what a role means in every system a person can touch, so that "add a developer" or "hire a support agent" is a decision made once, here, and executed by the machinery in authentik/ (directory, Cloudflare Access, GitLab, PostgreSQL, sv1, mail) and by the platform back-office (admin.guestget.com).

Two rules make the catalogue work at 3 people and at 30:

  1. Roles are functions, not titles. A CTO is a person in infra, tech-leads and management; a title is HR's concern, a role is a set of permissions. A person holds several roles; a role never holds a person's name.
  2. A role's meaning lives here and in code; who holds it is an operation. Changing what support can do is a merge request on this repository or on guestget, reviewed. Giving Ana the support role is two clicks in the directory's own interface (https://id.profuse.ro/if/admin → Directory → Users, then Groups), which the people role may do without touching git. Every other system reads that: Access from the groups claim at her next login, SSSD and PostgreSQL over LDAP, GitLab within five minutes, mail within fifteen. Nobody grants access inside one of those systems by hand; the quarterly review (access-review.md) is what catches the exception that slips through.

The catalogue#

Groups are slugs: they become the LDAP cn, the Unix group, the value in the groups claim. Every role has an owner, who approves membership and answers for it at the review.

role (group)whoowner
infraoperates production: platform / SRE, and the CTOCTO
tech-leadsseniors who merge, release and read production dataCTO
developersbackend, frontend, mobile engineersCTO
qatesters, test automationCTO
productproduct managers, designersCEO
supportL1/L2 support, customer success, implementationhead of customer
financebilling, invoices, payouts, accountingCEO
salesaccount executives, partnerships (affiliate agents)CEO
marketingsite, blog, content, campaignsCEO
peopleHR and administration: onboarding, offboarding, the directory's peopleCEO
managementCEO and whoever needs the whole picture read-onlyCEO

Do not add cto, ceo, senior-*, junior-*. Seniority is a title. If a senior needs more than a mid, that "more" is a role (tech-leads), and the mid gets it the day they are trusted with it.

What each role reaches#

Access has three levels (onboarding.md): identity alone (any device); identity on a managed device (WARP on, posture); identity, device and the private network (SSH, databases).

systeminfratech-leadsdevelopersqaproductsupportfinancesalesmarketingpeoplemanagement
directory administration (/if/admin)read + outposts (device)add and move people (device)
Cloudflare account, Zero Trustmember
sv1 SSH + sudoyes (network)
PostgreSQL productionread/write (network)read-only (network)
PostgreSQL stagingread/writeread/writeread-onlyread-only
handbook handbook.guestget.com (this page, onboarding, offboarding, the review; the first three also in Romanian at /ro)yesyesyesyesyesyesyesyesyesyesyes
handbook /who-has-what (names against roles)yesyesyes
GitLab guestget group (the product)ownermaintainerdeveloperdeveloperreporterreporterreporterreporter
GitLab profuse group (this repository: the infrastructure, and this catalogue's source)ownermaintainerreporterreporterreporterreporterreporterreporterreporter
Sentryadminadminmembermembermembermember
Zabbix, Traefik, mailcow adminyes (device)Zabbix read (device)
staging appyesyesyesyesyesyesyesyesyesyes
back-office admin.guestget.com (platform role)super_adminsuper_adminproductsupportfinancesalescontentmanagement
affiliate portal admin (agents)yesviewpaymanage, impersonateview
mail: personal mailbox first@guestget.comyesyesyesyesyesyesyesyesyesyesyes
mail: shared addresses (see below)security@support@, hello@billing@sales@, partners@, hello@press@jobs@, hr@legal@
adding and moving people (the directory's interface)yes

Read it by column: hiring a support agent = groups = ["support"] = the handbook, the staging app, the back-office with the support platform role, Sentry as member, GitLab as reporter (to file bugs), a mailbox, and support@ / hello@ in their inbox. Nothing else, and nothing to remember.

Two rows are worth reading twice. The handbook is everyone's: it is this page, the onboarding page and the rest of docs/, published at handbook.guestget.com and gated by nothing but a directory identity, so nobody needs an account anywhere else to read how the company works. Its one restricted page, /who-has-what, is the nominal list — which person holds which role — generated from the directory and open to infra, people and management, the three roles that administer, answer for, and are accountable for membership. GitLab is the other way round: it is where the product is built, and finance and sales no longer hold an account there. They held one for a single reason — the "Start here" tile used to point into the repository, so their first day was a login wall — and publishing the handbook removed the reason.

The handbook is in two languages#

Everything in this repository is written in English, and docs/ro/ is the one exception. The reason is who each is for: a runbook is read by the people who maintain the systems, and this handbook is read by everyone the company hires. The pages a colleague needs in order to join, understand and leave — onboarding.md, this catalogue and offboarding.md — are translated, and served at handbook.guestget.com/ro. Every page carries the link to itself in the other language.

Two pages stay in English, and each says so where its Romanian version would be:

  • the quarterly review (access-review.md) is a procedure a member of infra performs, and every artefact of it — the issue, the merge request, the output of the commands it asks you to read — is English already;
  • the nominal list (/who-has-what) is names, usernames and group slugs, which are the same string in any language, and its address is what the Access policy matches. A copy at another address would be the same list of names with none of the gate.

English is the source of truth, and not by convention alone: each Romanian file records the sha256 of the English file it was translated from, and scripts/build-handbook.py fails the build when the two no longer match, naming the file and the command that records the new hash. Editing an English page therefore breaks the pipeline until someone carries the change across — which is the point.

Where each mapping is executed#

systemmechanism
the directory itselftwo roles in authentik/rbac.tf: directory-people (create a person, reset their password, move them between roles) for people, directory-operations (read everything, run the outposts) for infra. No group is a superuser; akadmin, which is not a person, is the break-glass account, and every structural change is an OpenTofu apply
Access, WARP, SSH, DB gatewayone Access group per role on the groups claim (cloudflare/accounts/profuse/access.tf), one policy per level; Gateway rules per role for the databases (gateway.tf)
PostgreSQL rolespg-directory-sync (sv1, every 5 min): infra rw everywhere; tech-leads ro prod, rw staging; developers and qa ro staging; a demotion is a revoke
the handbookdocs/ rendered by scripts/build-handbook.py and published to Cloudflare Pages by the handbook:deploy job on every change to main; one Access application for the site (directory_everyone) and a second one for /who-has-what*, which a more specific path puts ahead of the first. The nominal list is read from the directory's API at build time, and a daily schedule rebuilds it. The Romanian pages are docs/ro/, published under /ro; each one records the hash of its English source and the build fails when they diverge (--refresh-translation-hashes records the new one)
GitLabgitlab-directory-sync (sv1, every 5 min): the account is created before the first login, the highest role sets the access level on guestget and on this repository, a leaver is blocked and dropped from both. finance and sales get no account
SentrySAML through Access, the Sentry roles on the SaaS application; the role inside Sentry is member by default, admins by hand (listed in the review)
ZabbixSAML through Access on a managed device for infra and tech-leads; the Zabbix role from the groups claim (infra → superadmin, tech-leads → user)
sv1SSSD simple_allow_groups = infra, sudoers %infra
maila mailbox per active person (mailcow template, first SSO); shared addresses are aliases whose recipients are the role's members, reconciled by mailcow-directory-sync (every 15 min)
back-officetoday: is_super_admin + a platform_role column with four agent abilities, password login, no SSO — see the next section

The back-office is the gap#

admin.guestget.com is where support, finance, sales and product will spend their day, and it is the one system that does not know the directory: staff log in with a password, the platform_role column can only be set from a seeder, and every section except the affiliate agents is super-admin-only. Three changes, in guestget, in this order:

  1. SSO. Staff log into the back-office through authentik (OIDC; Access already gates the hostname). Password login stays only for the seeded break-glass super-admin. The groups claim is read at every login and sets platform_role — the column becomes a cache of the directory, never edited by hand.
  2. A platform-role catalogue that matches this page. config/platform.php grows from four agent abilities to sections: organizations.view, organizations.manage, users.view, bookings.view, subscriptions.*, invoices.*, agents.*, content.* (blog, landing, websites), analytics.view, impersonate. Roles: super_admin (infra, tech-leads), support, finance, sales, content (marketing), product, management (read everything, change nothing). The admin app already gates its navigation on abilities; only the map grows.
  3. Impersonation with a reason. Support impersonating a hotel is the most sensitive thing the company does. Today it needs super-admin and leaves no reason. It becomes a support ability that requires a ticket reference, lasts one hour, is read-only unless the role says otherwise, and is written to the admin audit log with the reason — the same shape the affiliate-agent impersonation already has.

Mail#

One domain, guestget.com. A person has exactly one mailbox, firstname@guestget.com, created by the directory at their first SSO login and switched off the day they leave. Nobody has a second password for mail: clients use app passwords.

Shared addresses are aliases, not mailboxes: support@, hello@, billing@, sales@, partners@, press@, jobs@, hr@, legal@, security@. Their recipients are the members of a role, kept in sync from the directory — joining support puts support@ in your inbox, leaving removes it, and no shared password ever exists. Today support@, billing@, legal@ and app@ are real mailboxes with their own passwords; mailcow-directory-sync reports what each would become and never converts a mailbox on its own — delete the mailbox (after exporting it) and the next run creates the alias. app@ / noreply@ / alerts@ stay mailboxes: they are the platform's own senders and receivers, with app passwords held in the secrets store, not by a person. When support outgrows an inbox, a helpdesk tool takes over support@ — the alias just points somewhere else.

Naming, and the domain#

Usernames are firstname (or firstname.lastname at the first collision), lowercase, chosen once: the same string is the GitLab handle, the PostgreSQL role, the Unix user and the mailbox. The directory's e-mail is the person's @guestget.com address.

The directory, GitLab and the Access team domain still carry the profuse.ro name from the company that built them. They move to id.guestget.com, git.guestget.com and a guestget Zero Trust team as their own migration — DNS, certificates, the OIDC callback URLs, WARP re-enrolment — after the roles above are in place, not before: a rename with three roles is a morning, a rename mid-hiring is a week.

How this grows#

  • Today: this catalogue in git, the people in the directory's interface, the quarterly review.
  • When someone starts asking for access rather than being given it: authentik's access-request workflow (2026.8) lets a person request a role and its owner approve it in the portal, with the grant recorded there instead of in a conversation.
  • When there is an HR system: it feeds the directory over SCIM and even the two clicks go away. The catalogue does not change shape at any size; only who performs the assignment does.