01Infrastructure
Mekovault runs on AWS Lightsail in us-east-1, on four machines connected by a private network. Only one of them receives Internet traffic.
| Host | Role | Services | Exposure |
|---|---|---|---|
mekovault-app | Application | nginx, Next.js, FastAPI services, addons | Only exposed host: ports 80/443 behind Cloudflare |
mekovault-data | Data | PostgreSQL 16, Redis (2 instances), RabbitMQ | Private network only |
mekovault-vault | Secrets | Self-hosted Infisical, one project per tenant | Private network only |
mekovault-obs | Observability | Grafana, Loki, Prometheus, Tempo, OpenTelemetry Collector | Private network only |
- Administration exclusively over Tailscale (WireGuard). No host has SSH open to the Internet.
ufwfirewall active on every machine with a default-deny policy.- Internal services (PostgreSQL, Redis, RabbitMQ, Infisical, Grafana and the FastAPI processes) listen on loopback or the private interface. nginx is the only process serving public traffic.
- Cloudflare terminates TLS and filters traffic before it reaches nginx.
02Service architecture
The web interface is a Next.js 16 application. The backend is a set of FastAPI services on Python 3.12 with async SQLAlchemy, each with a narrow responsibility.
| Service | Responsibility |
|---|---|
svc-auth | Authentication, sessions, MFA and password reset |
svc-tenancy | Companies, memberships, roles, resellers and tenant deletion |
svc-requests | Joiner, mover and leaver requests; approval flow |
svc-tickets | Support tickets and their follow-up |
svc-billing | Plans, subscriptions and invoicing |
svc-notifications | Email and user notifications |
| addons | Connector modules. Example: super-workspace, which operates on Google Workspace |
Events and background jobs
- Services communicate through events on RabbitMQ, using the topic exchange
mekovault.events. - Heavy work (provisioning, deletions, exports) is recorded in a job ledger in PostgreSQL and executed by a single worker.
- Each job retries with exponential backoff. Its steps are idempotent: re-running a completed step does not produce duplicate effects on the directory.
- When a job exhausts its retries an alert is raised and the job stays visible with its last error for manual intervention.
03Identity and session
Mekovault users (IT administrators, approvers, resellers) authenticate with Google OAuth or with email and password. The session is designed so that a stolen token is short-lived and revocable.
| Mechanism | Detail |
|---|---|
| Sign-in methods | OAuth with Google, or email and password |
| Access token | JWT signed with RS256, 15-minute lifetime, with role and MFA-state claims |
| Refresh token | 30-day lifetime. Lives only in an HttpOnly cookie (unreachable from JavaScript). Rotated on every use and individually revocable by jti in Redis. Inherits the session's MFA state and re-validates the user's company membership on every renewal. |
| MFA | TOTP (standard authenticator apps). Limit of 5 attempts per minute; 15-minute lockout after 10 consecutive failures. |
| Sensitive operations | Require a session with verified MFA, even if the user is already authenticated. |
| Password reset | Single-use token with a 30-minute lifetime. |
Practical consequence: a leaked access token expires within 15 minutes at most; the refresh token cannot be exported from the browser; and revoking the jti in Redis ends the session immediately, without waiting for anything to expire.
04Multi-tenant isolation
Mekovault is multi-tenant: several customers share the same infrastructure. Isolation between them is enforced in independent layers, so a defect in one layer does not expose another customer's data.
| Layer | Control |
|---|---|
| Data model | Every row holding customer data carries company_id. |
| Application | Every query filters by the company carried in the session token. Never by a parameter sent by the client. |
| Database | PostgreSQL Row-Level Security as an additional barrier: even if a query forgot the filter, the database returns no rows from another company. |
| Superadmin | Platform role separate from customer roles. Its actions require code confirmation and verified MFA. |
| Resellers | A reseller sees and operates only on the tenants it manages. |
05Provider credentials and usage audit
To operate on your directory, Mekovault receives credentials with administrative permissions: the JSON of a Google Service Account with domain-wide delegation. They are the most sensitive asset we handle.
- Stored encrypted in Infisical (self-hosted, on
mekovault-vault), in a dedicated project per tenant. - Never written to the database, logs or API responses. The database stores only a reference to the secret.
- Loaded into memory at the moment of calling the provider and discarded when the operation ends.
Immutable record of every use
Every call to Google with a customer's credentials writes a row to the credential_usage_log table.
| Field | Content |
|---|---|
| Tenant | company_id of the customer owning the credential |
| Provider | Google Workspace |
| Operation | Action executed, for example create_user, suspend_user, add_group_member |
| Scopes | Scopes or permissions consumed by that operation |
| Who | User who originated the action, or the automated process (worker, timer) that executed it |
| Reason | Reference to the request, ticket or task that justified the call |
| Result | Success or failure, with error code where applicable |
| Latency | Duration of the provider call in milliseconds |
- The table has PostgreSQL triggers that reject any
UPDATEorDELETE. Not even the Mekovault team can edit or remove an individual row. - Each row has a deterministic id derived from the operation. A retry of the same job does not produce a second row, so the log neither inflates nor contradicts itself.
- A burst of failures against a tenant's credentials raises an alert to the operations team.
Retention
| Parameter | Value |
|---|---|
| Minimum | 24 months |
| Maximum | 84 months |
| Configuration | Per tenant, within that range |
| Tenant deletion | Deleted with the tenant, or after 24 months, whichever comes first |
| Purge | Weekly, only of rows outside the configured period. Every purged batch is recorded in the audit trail. |
Export for auditors
The log exports as CSV or JSON. Each file is signed with HMAC-SHA256 and includes the identifier of the key used, so an auditor can verify the file was not altered after it was generated.
06Permissions Mekovault requests
Mekovault asks only for the permissions it needs to manage users, groups and organizational units. It does not request access to mail, files, calendar or any user content.
| Scope | What it is used for |
|---|---|
admin.directory.user | Create, suspend, reactivate and modify user accounts; set an initial password and force its change. |
admin.directory.group | Create groups and lists, and add or remove members during joiner, mover and leaver flows. |
admin.directory.orgunit | Read and move users between organizational units according to the onboarding or offboarding flow. |
Calls are made with the Service Account impersonating the delegated administrator the customer designates when connecting the domain. That administrator is recorded in every credential_usage_log row.
07Initial passwords and resets
- When Mekovault creates an account or resets a password, the temporary password is visible to the administrator who executed the action for 15 minutes.
- After that it is purged from the system. It cannot be recovered: if lost, a new one is generated.
- Passwords are never written to logs, events or the credential usage log.
08Tenant deletion
A customer can request full deletion of its tenant (right to erasure, GDPR art. 17 and Chilean Law 21.719). The process is designed so credentials disappear before any other data.
- A tenant administrator requests deletion. The request is queued as a job.
- The tenant becomes inaccessible immediately: its sessions and new sign-ins are rejected.
- The job revokes the tenant's secrets and deletes its Infisical project. From this point Mekovault can no longer call the customer's directory.
- Only then is the tenant's data deleted in cascade from PostgreSQL: users, requests, tickets, configuration.
- The deletion is recorded in the platform audit trail.
- Administrators receive a confirmation email detailing what was deleted.
09Addons: modules with limits
Provider connectors and other extensions are packaged as addons. A defective addon must not be able to degrade the whole platform, and the addon registry enforces that rule.
| Stage | Control |
|---|---|
| Declaration | Each module is described in a manifest with its ports, routes, tables, migrations and health check. |
| Validation | Automatic before activation: manifest schema, port, route and table collisions with other modules, applicable migrations and health check response. |
| Execution | Runs with memory and CPU limits. A module that overflows is restarted without affecting the others. |
| Supervision | Health check every 60 seconds, with the result visible in the operations panel. |
| Kill switch | Any module can be disabled without a deploy: within 15 seconds or less its routes return 503 and the rest of the platform keeps operating. |
10Perimeter and browser
| Control | Effect |
|---|---|
| HSTS | The browser connects to the domain only over HTTPS, even if the user types http. |
| Content-Security-Policy | Restricts the origins from which the portal may load scripts, styles and connections. |
| X-Content-Type-Options: nosniff | Prevents the browser from reinterpreting a file's type. |
| frame-ancestors 'none' | The portal cannot be embedded in an iframe on another site (clickjacking). |
| Restricted CORS | Only Mekovault origins may call the API from a browser. |
| No source maps in production | Frontend source code is not published. |
| No public API documentation | The services' OpenAPI schemas are not exposed in production. |
| Errors without traces | Error responses include no stack traces or internal details. |
11Operations and observability
| Signal | Destination |
|---|---|
| Structured logs (JSON) | Loki, queried from Grafana |
| Metrics | Prometheus |
| Distributed traces | OpenTelemetry into Tempo |
- A database backup is taken before every deploy.
- Services are deployed in a controlled way; a failed deploy is rolled back to the previous backup.
Hygiene timers
| Task | What it does |
|---|---|
| PII purge | Deletes personal data that has completed its retention period. |
| Ticket closure | Closes resolved tickets with no activity after the configured period. |
| Secret expiration | Warns administrators 30, 7 and 0 days before a provider credential expires. |
| Usage log purge | Weekly; deletes only rows outside the configured retention and records the batch. |
12Security layers summary
| Layer | Controls |
|---|---|
| Perimeter | Cloudflare, nginx as the only public process, ufw |
| Network | Lightsail private network, Tailscale for administration, services on loopback |
| Identity | 15-minute RS256 JWT, rotated and revocable HttpOnly refresh, TOTP MFA with rate limit |
| Application | Filter by the token's company_id in every query |
| Data | Row-Level Security in PostgreSQL 16 |
| Secrets | Self-hosted Infisical, one project per tenant, never in DB or logs |
| Audit | Immutable credential_usage_log, export signed with HMAC-SHA256 |
| Modules | Validated manifest, resource limits, supervision every 60 s, kill switch |
| Operations | Pre-deploy backups, hygiene timers, logs, metrics and traces |
13What is in progress
We prefer to say what is missing before a questionnaire asks. These items are being worked on and should not be assumed to be in place until this page moves them to the relevant section.
| Item | Status |
|---|---|
ENVIRONMENT=production on every service (today some run with hardened development configuration) | In progress |
| Operational alerts to Slack | In progress |
| Dedicated PostgreSQL role per addon, so each module reaches only its own tables | In progress |
| SOC 2 | Not planned for now; the framework guides our controls |
FAQFrequent audit questions
Direct answers to what security teams and auditors usually ask.
- Where do my credentials live?
- In Infisical, a self-hosted instance on the
mekovault-vaultmachine, inside the AWS Lightsail private network in us-east-1. Each tenant has its own project. The database stores only a reference to the secret, never its value. - Who can see them?
- No person sees them in normal operation. Services load them into memory when calling the provider and discard them afterwards. Access to the vault machine requires Tailscale and is restricted to the platform team. Temporary passwords generated by Mekovault are visible to the customer's administrator for 15 minutes and then purged.
- What happens if I leave Mekovault?
- An administrator requests tenant deletion. Access is cut immediately; then secrets are revoked and the Infisical project is deleted; only after that is the data deleted in cascade from PostgreSQL. The event is recorded in the platform audit trail and administrators receive an email with the details. Sub-processors holding residual data are listed on the sub-processors page.
- How do I prove my permissions were not used for something else?
- With
credential_usage_log: every call to Google with your credentials is recorded with operation, scopes, originator, reason, result and latency. The table accepts neither UPDATE nor DELETE. You can export it as CSV or JSON signed with HMAC-SHA256 and verify the signature with the included key id. - Is a stolen session token useful for anything?
- The access token expires in 15 minutes. The refresh token lives only in an HttpOnly cookie, is rotated on every use and can be revoked by
jtiin Redis. Sensitive operations additionally require verified MFA in that session. - Can a faulty module take down the platform?
- It is not designed to be able to. Each addon runs with memory and CPU limits, is supervised every 60 seconds and has a kill switch that disables it within 15 seconds or less without a deploy. The remaining services keep operating.