Goooy Administrator Guide
Everything an administrator needs to run, secure, and operate Goooy: your self-hosted alternative to Microsoft Exchange / Office 365.
Goooy is a complete groupware suite you host yourself: mail, calendar, contacts, tasks, notes, chat, files, video meetings, collaborative office, and an admin console, all from one application, one data model, and one Helm chart. You own the servers, the data, and the keys.
This guide is for the people who run Goooy. For the platform’s technical
foundations (the architecture, deploying with Helm or Docker Compose, and the
configuration essentials), see the Developer Guide. For
the end-user’s view of the apps, see the User Guide. For deep
technical detail, the docs/ folder has dedicated references (architecture,
security, mail server, tenancy, deployment, migration, and the native-client
protocols).
Contents
- The admin console
- Organizations, domains & users
- Authentication & access policy
- The mail server
- Native clients & device management
- Migrating from another system
- Multi-tenancy & isolation
- Security & compliance
- Backups & operations
- Self-service signup & your marketing front
The admin console
Sign in as an admin and you get an RBAC-gated Admin area (the REST surface is
/api/v1/admin, restricted to SYSADMIN / ORGADMIN and scoped by organization).
From it you can:
- Manage organizations, domains, users, and groups (create, edit, delete).
- View a stats dashboard of platform/organization usage.
- Run Mobile Device Management: list enrolled devices and request remote wipes.
- Integrate a directory: bulk-import users and synchronize from LDAP.
- Configure per-org SSO and security policy (2FA mandate, SSO enforcement).
- Verify domain ownership via DNS TXT records.
- Run migrations from other mail systems (Admin ▸ Migrations).
Organizations, domains & users
Goooy’s tenancy spine is Organization → Domain → User. Each user owns one mailbox with a typed folder tree (mail, calendar, contacts, tasks, notes, files), so one storage-and-permission model backs every app. Deleting an org, user, or mailbox cleanly cascades to everything beneath it.
Roles (RBAC)
| Role | Scope |
|---|---|
SYSADMIN | The whole platform: all organizations |
ORGADMIN | Their own organization only |
USER | A regular end user |
ORGADMIN actions are automatically scoped to their orgId; an ORGADMIN cannot
create organizations or grant SYSADMIN. Every multi-tenant query filters by the
caller’s organization, so tenants never see each other’s data.
Adding users
Create users individually in the console, bulk-import them, or sync from
LDAP with ldapts. Each new user is automatically provisioned with a mailbox and
the default folder tree. Passwords follow your policy (see below).
Domain ownership verification
Before a domain handles real mail, an admin can prove control of it the same way Google Workspace and Microsoft 365 do:
- The console shows a TXT record to publish (
_goooy-verify.<domain>with a stable token value). - Publish it in your DNS.
- Click Verify now. Goooy does a live DNS lookup and marks the domain verified.
This is org-scoped and audited; an ORGADMIN can only verify their own org’s domains.
Authentication & access policy
Goooy supports the full range of modern access controls. The defaults are safe; tighten them per-organization as needed.
Passwords
- Hashed with bcrypt (cost 12), the same hashes Dovecot verifies, so there’s one credential store across webmail and native IMAP/POP3.
- A password policy is enforced on every set/change/reset/signup: a minimum
length (
PASSWORD_MIN_LENGTH, default 10) and an optional Have I Been Pwned breach check (k-anonymity: only a SHA-1 prefix ever leaves the server, and it fails open so a HIBP outage never blocks a password change). - Self-service password reset is enumeration-safe and revokes all other sessions on use.
Multi-factor authentication
- TOTP authenticator codes with one-time recovery codes, plus FIDO2/WebAuthn passkeys (with clone detection).
- Set
Organization.requireMfato mandate 2FA for an org. Users are walked through enrollment and blocked from the app until they finish.
Brute-force & account-takeover defense
- Two layers of throttling: a per-IP rate limit on login/MFA endpoints, and a per-account lockout (after a threshold of failed attempts the account locks for an exponentially-growing window) that defeats distributed credential-stuffing.
- Login audit: every attempt is recorded (IP, user-agent, result) and users can see their recent sign-ins in Settings → Security.
- New-device alerts: the first time a device signs in, the account gets a
“new sign-in” email; password and MFA changes also notify the user (gated by
SECURITY_EMAILS_ENABLED).
Single sign-on (per-org OIDC)
Configure a generic OIDC connection per organization (Authorization Code flow). The org is resolved from the user’s email domain (home-realm discovery), users are just-in-time provisioned if their domain is hosted, and the client secret is write-only (sealed at rest, never returned).
You can enforce SSO for an org (ssoEnforced) to disable password login
entirely, with two safety rails so you can’t lock yourself out: it only takes
effect while a usable SSO connection exists, and SYSADMIN accounts always keep
password login as a break-glass.
See Self-service onboarding & external auth for the full auth API, and Security for the deep reference.
The mail server
By default Goooy is self-contained: it accepts inbound mail on its own SMTP
(2525) and LMTP (2526) listeners, relays outbound via nodemailer, scores spam with
a built-in rule-based heuristic, and delivers locally between hosted users, so
internal mail works with zero external infrastructure.
For real internet mail, enable the Postfix + Dovecot + Rspamd stack and the API becomes the delivery hub of a full mail server:
helm upgrade goooy ./deploy/helm/goooy --set mail.enabled=true
Internet ─SMTP:25─▶ Postfix ──milter──▶ Rspamd ──LMTP──▶ api ─┬─▶ Postgres (web UI; spam→Junk)
clients ─587/465─▶ (MTA, SASL) (scan+DKIM) └─▶ Dovecot ─▶ Maildir (IMAP/POP3)
- Postfix accepts mail for your hosted domains (looked up live in Postgres), scans through the Rspamd milter (SPF/DKIM/DMARC + spam scoring), and delivers to the API over LMTP.
- The API writes the message to Postgres (the webmail store, filing Rspamd-flagged spam to Junk) and mirrors it into Dovecot so native IMAP/POP3 clients see it.
- Outbound relays back through Postfix, DKIM-signed by Rspamd.
Anti-phishing is built in: inbound sender authentication results are stored per message; an external-sender banner flags mail from outside your domains; own-domain spoofs that aren’t authenticated are filed to Junk with a strong warning; users’ “Report” / “Not junk” actions train Rspamd’s Bayes filter; and the reader warns on deceptive links, homograph/punycode hosts, and display-name impersonation, with remote content blocked-by-default and proxied.
Safe links & attachment sandboxing. Two further inbound defenses can be switched on per organization under Admin ▸ Mail security (both are off by default):
- Safe links rewrites every link in inbound external mail and re-checks the destination at click time: blocklisted URLs get a block page, suspicious ones (IP-literal hosts, embedded credentials, punycode look-alikes) get a warning interstitial, and clean ones redirect straight through: every click is audited. Sent copies, internal mail, and signed or encrypted bodies are left untouched.
- Attachment sandboxing statically detonates inbound attachments on top of the always-on antivirus scan: executables detected by content, Office macro projects, password-protected and nested archives, and scripted HTML/SVG. Malicious mail is filed to Junk, and any flagged attachment is quarantined (downloads blocked) until an org admin releases it.
Operational caution: never put
permit_mynetworkson an internet-exposed SMTP listener. That creates an open relay. The shipped configs separate the public :25 port from an internal-only submission port for exactly this reason. Full detail and limits are in Mail server anddeploy/mail/README.md.
Native clients & device management
Goooy speaks the standard protocols, so your users keep the apps they already have. All are mounted at their well-known roots and use HTTP Basic (email + password) where the protocol expects it:
| Protocol | Path | Clients |
|---|---|---|
| Autodiscover / autoconfig | /autodiscover/*, /.well-known/autoconfig | Outlook, Thunderbird, mobile mail (self-configure from email + password) |
| CalDAV | /dav/cal | Apple Calendar, Thunderbird, phones |
| CardDAV | /dav/card | Apple Contacts, Thunderbird, phones |
| Exchange ActiveSync (EAS) | /Microsoft-Server-ActiveSync | iOS Mail/Calendar/Contacts (a functional subset) |
| MAPI/HTTP | /mapi/emsmdb, /mapi/nspi | native Outlook for Windows |
| IMAP / POP3 / SMTP | (mail stack) | any mail app, when the mail server is enabled |
| MS-FSSHTTP / WebDAV (Office) | /_vti_bin/cellstorage.svc, /dav/office/<id> | desktop Word / Excel opening Office docs |
Modern auth. Goooy is now its own OAuth2/OIDC provider: native clients can authenticate with an OAuth Bearer / XOAUTH2 token or a revocable app-password (Settings → Security) instead of the account password, and third-party apps can offer “Sign in with Goooy”. Whether the iOS/Android Outlook app accepts a self-hosted issuer is still being validated on real devices; until confirmed, advise iPhone/iPad users to use the built-in Apple Mail app (or an app-password with any IMAP client).
Mobile Device Management. Devices that enroll over ActiveSync are tracked. From the MDM console you can list enrolled devices and request a remote wipe. The device is flagged and wiped on its next sync.
The helpdesk
A helpdesk is a workflow layer over a shared mailbox, not a separate system. Create the shared mailbox first, share it with the agents, then enable the helpdesk on it under Admin ▸ Helpdesk.
- Agents are exactly the people the mailbox is shared with. A read-only grantee can open tickets but cannot assign, reply or change status.
- Queue settings hold the ticket key prefix (
TCK), business hours, the first-response and resolution targets in business minutes, the auto-close period, and the acknowledgement sent to a new sender. - Canned replies are scoped to one queue or shared across the organization.
- AI triage is opt-in per queue. It suggests a priority and tags; it never sends anything by itself.
Mail delivery is untouched: every message still lands in the mailbox’s Inbox and stays visible over IMAP, ActiveSync, MAPI and JMAP. Turning the helpdesk off leaves the mailbox and all of its mail exactly as they were.
Mail authentication & reporting
Publishing SPF, DKIM and DMARC is step one; knowing whether they actually work is
step two. Point the rua= address in your DMARC record at a mailbox on the
domain, and Goooy parses each aggregate report as it is delivered.
Admin ▸ Mail authentication then shows, per domain: the pass rate, SPF and DKIM alignment, and the sending sources that fail, which is how you tell a forgotten legitimate sender from someone spoofing you. SMTP TLS reports (TLS-RPT) arrive the same way and surface failed or downgraded connections.
Goooy can also host an MTA-STS policy per domain at
/.well-known/mta-sts.txt, so other providers refuse to hand your mail over an
unencrypted connection.
Provisioning with SCIM and LDAP
Three ways to keep accounts in step with your directory:
- SCIM 2.0 at
/scim/v2, authenticated with a per-organization bearer token. Your identity provider creates, updates and deactivates users and groups as staff join, move and leave. - LDAP / Active Directory sync pulls users and groups on a schedule.
- CSV bulk import for a one-off load.
Combine any of them with per-org SSO so that disabling someone centrally removes their access everywhere, native clients included.
Migrating from another system
The Goooy Mover moves an existing company’s data into Goooy from a live IMAP server or from uploaded export files: an admin maps each source mailbox to a Goooy user (missing users are auto-provisioned), clicks start, and it transfers in the background with a live per-user status tracker. The admin UI is Admin ▸ Migrations.
What it preserves and guarantees:
- Folder hierarchy: well-known folders (Inbox, Sent, Trash, Junk, Archive) map to their Goooy roles; deeper folders are recreated as children.
- Dates and state: original received dates and read/flag status, so a migrated mailbox looks like the original.
- Resumable & idempotent: it checkpoints progress, so killing the API
mid-batch resumes from where it left off, and re-running never creates
duplicates (deduped by
Message-ID, or a UID for calendar/contacts).
The live-server path works against Gmail, Microsoft 365, Dovecot, Zimbra, and most IMAP servers (use an app-password where the provider requires one: the wizard has one-click connection presets for the common providers). The file-upload path accepts Outlook .pst (mail + calendar + contacts), .mbox (mail, including a Google Takeout export: the easiest way off a personal Gmail account), .ics (calendar) and .vcf (contacts), with the format auto-detected from the file’s content. The flow is Source → Accounts→ users (with a per-row credential Test, or per-file upload) → Run → live Dashboard with per-row retry and batch pause/cancel. All mutations are audited. Full detail and the roadmap (CalDAV/CardDAV, Microsoft 365 Graph, Gmail API, tenant-to-tenant) are in Migration.
Multi-tenancy & isolation
By default Goooy is logically multi-tenant: every query is scoped by orgId and
guarded by RBAC, with all tenants sharing one database. For stricter requirements,
an opt-in hard-isolation mode gives each tenant its own PostgreSQL schema or
its own database:
TENANT_ISOLATION = shared (default) | schema | database
A control-plane catalog resolves the right connection per request (req.db), so the
shared-database default is completely unchanged when you don’t opt in. This lets you
offer per-tenant data residency or contractual isolation without running separate
deployments. See Tenancy.
Security & compliance
A condensed checklist. The authoritative reference is Security.
-
Transport. TLS terminates at the Ingress (optionally automated with cert-manager). TLS to the backing stores is URL-driven and needs no code change:
?sslmode=requireonDATABASE_URL,rediss://for Redis,https://S3 endpoint. -
Headers. Helmet on the API; nginx sets a strict CSP,
X-Frame-Options: SAMEORIGIN,Referrer-Policy, and aPermissions-Policyscoping camera/mic/display-capture to self. CORS is scoped toPUBLIC_WEB_URL(native protocol paths are intentionally exempt, don’t “fix” that). -
End-to-end mail security (S/MIME). Each user can have a self-signed X.509 identity to sign/verify (RSA-SHA256) and encrypt/decrypt (hybrid RSA-OAEP + AES-256-GCM, multi-recipient); the private key is sealed at rest and never returned in the clear.
-
Email rendering. HTML mail is DOMPurify-sanitized and constrained by the nginx CSP. Keep both.
-
Secrets at rest. S/MIME keys, SSO client secrets, and migration credentials are AES-256-GCM sealed using
JWT_SECRET. Supply your own Secret; rotateJWT_SECRETdeliberately (it invalidates sealed values). -
Containers run non-root under a restrictive security context.
-
Data sovereignty. Goooy is self-hosted end to end. You choose the region and the provider for every store. See Data sovereignty.
-
Retention. Per-organization policies age mail, files and messages out on a schedule you set. The sweep runs in the background and is audited.
-
Legal hold. A hold on a custodian preserves their data and overrides retention until it is lifted, and exports everything as JSON for review.
-
Data-loss prevention. Org-wide scanning across mail, chat and files for the patterns you configure.
License: AGPL-3.0-or-later.
Backups & operations
- What to back up: PostgreSQL (the system of record) and your S3 bucket (file
and attachment blobs). Redis is ephemeral and needs no backup. The chart can run a
nightly
pg_dumpCronJob (backup.enabled=true). - Health probes: the API exposes
/healthz(liveness) and/readyz(DB-backed readiness) for Kubernetes. - Scaling: the API is stateless (JWT auth) and fans realtime out over Redis
pub/sub, so it scales horizontally. Enable the HPA (
autoscaling.enabled=true, needs metrics-server) and a PodDisruptionBudget for safe rollouts. - Observability: structured JSON logs (pino).
- Schema changes: the API container runs
prisma migrate deployon start, so committed migrations apply automatically on rollout. - Don’t kill the ingest ports (2525/2526) to “free” them in a running environment. That’s how inbound mail arrives.
Self-service signup & your marketing front
Goooy can run a public self-service signup flow so customers create their own
organization. It’s disabled by default. Set SELF_SIGNUP_ENABLED=true to turn
it on. The endpoint atomically creates an Organization, its first Domain, and an
ORGADMIN user (never SYSADMIN), and returns a live session. It’s rate-limited
per IP as an abuse gate.
The bundled web app already ships a public /signup page that drives this flow
same-origin, so you don’t have to build it. And a static Astro marketing site
(apps/marketing) is included for the public front door. Enable it on its own
host:
helm upgrade goooy ./deploy/helm/goooy \
--set marketing.enabled=true \
--set marketing.host=www.example.com
Its “Get started” CTA links to the app’s /signup page. The full external-auth API
(login, MFA, SSO, sessions, domain verification), everything you’d need to build
your own signup front instead, is documented in
Self-service onboarding & external auth.
Get started
- New here? Read the User Guide for the end-user view of every app.
- Deploying or integrating the platform? The Developer Guide covers the architecture, Helm/Docker Compose deployment, and configuration.
- Browse the feature overview, security & compliance, and self-hosting pages.
- The full technical reference (architecture, data model, mail server, tenancy, deployment, migration, and the native-client protocols) ships with the product source.