Invite your team
Chapter 5 · about 10 minutes to read
You can run the platform solo, but you don't want to. This chapter covers the four moving pieces of multi-user Zyra: invitations, roles, removal, and the audit log that proves who did what.
Time: about 10 minutes. Prerequisites: you are an Org Admin or Super Admin.
The three roles
Zyra ships with three built-in invitation types (from backend/app/models/user_invitation.py, InvitationType enum):
super_admin— platform-wide administrator. Owns the global account, can manage every organisation, sees every invoice. Hand out sparingly.org_admin— full read/write inside one organisation. Can invite, remove, and reconfigure billing, devices, Virtual Servers, and SLA agreements.org_member— day-to-day operator inside one organisation. Can deploy and manage Virtual Servers, read metrics and logs, submit jobs. Cannot change billing, invite or remove users, or edit org-level settings.
Custom roles on top of these three are supported via the RBAC service (backend/app/services/rbac/).
Permission matrix (built-in roles)
| Capability | super_admin | org_admin | org_member |
|---|---|---|---|
| Sign in | yes | yes | yes |
| Deploy / start / stop Virtual Servers | yes | yes | yes |
| View VS metrics + logs | yes | yes | yes |
| Enroll / remove Compute Nodes | yes | yes | no |
| Invite users | yes | yes | no |
| Remove users | yes | yes | no |
| Edit org profile + billing | yes | yes | no |
| View invoices | yes | yes | no |
| Configure SLA agreements | yes | yes | no |
| View audit log | yes | yes | no |
| Manage other organisations | yes | no | no |
[VERIFY: confirm org_member can view invoices vs strictly admin-only — RBAC permission_engine has fine-grained checks not all front-end-exposed at launch]
Step 1 — Send an invitation
Open Settings → Team → Invite user. Fields: Email (becomes login ID), Name, Role (org_admin or org_member; super_admin is invited from the platform admin console), Organisation (pre-filled).
Submit. Zyra generates a 256-bit token (secrets.token_urlsafe(32)), records a user_invitations row with status = pending, sends the magic-link email, and sets expires_at = now + 72 hours per UserInvitation.default_expiry.
The 72-hour expiry
This is hard-wired in the model. Invitations expire 72 hours after creation — not 7 days, not a week. The 72-hour window is a SOC 2 / ISO 27001 control: short-lived credentials reduce blast radius if an invite email is intercepted.
If a teammate misses the window, resend from Settings → Team → Pending → Resend. That generates a fresh token and resets the clock; the old token is revoked.
Step 2 — The invitee accepts
The recipient clicks the link, lands on the acceptance page, sets their own password (Zyra never stores it before they type it), and is redirected to sign in. On the database side, the invitation flips status: pending → accepted, accepted_at is timestamped, and user_id is set to the new users.id.
Step 3 — Remove a user
- Revoke an invitation that hasn't been accepted: Settings → Team → Pending → Revoke. The row's
statusflips torevoked; the token stops working immediately. - Deactivate a user who already signed up: Settings → Team → Active → Remove. The user's
is_activeflag flips to false; their session is invalidated on next request; their Virtual Servers are NOT auto-terminated — reassign or terminate them explicitly.
[VERIFY: confirm UI exposes a single "Remove" button or splits revoke-pending from deactivate-active]
Custom roles
If the three built-in roles don't fit your structure, org_admin users can build custom roles inside their org: Settings → Roles → New role, name it (billing_only, read_only_auditor, etc.), attach individual permissions from the permissions table, assign users via Team → Edit user → Role. Custom roles live in custom_roles per organisation; permissions attach via role_permissions with allow/deny effects evaluated by permission_engine.py.
Step 4 — The audit log
Every state-changing action writes to the audit log. Open Settings → Audit Log. Filters: Actor, Action (user.invited, user.removed, vs.created, vs.terminated, invoice.generated...), Time window, Resource type / ID. Audit retention is 365 days. [VERIFY: confirm export-to-CSV button is exposed in audit log UI at launch]
What just happened
You know the three built-in roles, the 72-hour invitation expiry, the difference between revoking and deactivating, and where the paper trail lives.
Troubleshooting
- Invitee never received the email. Check spam first. If clean, Pending → Resend. If still nothing, the SMTP relay may be paused.
- Invitee says "this link has expired" within 72 hours. The token was already used or revoked. Resend.
- org_member can see something they shouldn't. Open Settings → Roles → org_member → Permissions; the RBAC engine is the source of truth. Check custom roles too.
- Audit log is empty for an action I know happened. Service-account actions log with the service principal; filter by Actor type = service.
Last reviewed: 2026-05-21