Architecture, tenancy and limits
For architects and IT teams evaluating Artavolo for an organisation: how it is built, how tenants are separated, how an organisation administers it, what the limits are and how it grows. Everything here describes the product as it ships; what is not built yet is listed at the end. Companion documents: security overview, single sign-on, backups, data API, Airtable-compatible API, AI agents.
In short
- Three ways to run it, one codebase. The hosted service (artavolo.com, OVHcloud, Germany), a managed private instance (one virtual machine per customer, EU), or self-hosted on your own servers (Docker or a plain LAMP server).
- Tenant = workspace on the hosted service, instance everywhere else. Hosted workspaces share servers and are separated by the application. An organisation that needs physical separation takes a managed instance or runs its own.
- The stack is deliberately plain: PHP 8 and MySQL/MariaDB (PostgreSQL for the data database), with optional Redis. No vendor cloud services are required, and nothing phones home.
- No record limit on any plan. On the hosted service, file storage is the quota that refuses an action. API requests are limited per token and per workspace.
Components
browser (SvGrid Base UI, Svelte) programs: REST, /v0 (Airtable-compatible), MCP (AI chat and agents)
| |
+------------------ HTTPS -----------------+
|
reverse proxy / TLS (Caddy, Apache, cPanel)
|
PHP application (stateless per request; sessions in PHP's session store)
| | | | |
application DB data DB uploads websocket server cron
(MySQL/MariaDB) (MySQL/ (per-workspace (live updates; (automations,
accounts, MariaDB or folders on a Pusher as the scheduled backups,
workspaces, PostgreSQL) volume) hosted fallback) imports, Airtable sync)
permissions, one table
audit log, per user
settings table
|
Redis (optional cache)
- Application database. Accounts, workspaces, bases, table definitions, permissions, API tokens, agents, the audit log, settings and rate-limit counters.
- Data database. Each table a person creates is its own SQL table (
database_<id>) with real column types and indexes, so filtering, sorting, grouping and paging happen in SQL. It can live on a separate server, over TLS, as MySQL/MariaDB or PostgreSQL (DB_DRIVER_DATA). - Files. Attachments are stored under
uploads/workspaces/<workspace id>/and served only through a permission-checked route (or a time-limited signed link for e-mail and webhooks). - Computed values on the server. Links are stored by record id. Lookups, rollups, counts and formulas are computed on the server as well as in the page, so the API, AI chat and agents read the same values people see.
- Work after the response. Webhooks, automation e-mails and webhook steps are sent after the request has answered, with timeouts, so a slow receiver never slows a person down.
- Background work. Cron (
app_crons.php) runs automations, scheduled backups, Airtable imports (resumable jobs) and the Airtable dual-run sync.
Tenancy model
The hierarchy
| Level | What it is | Who controls it |
|---|---|---|
| Instance | One installation: the hosted service, or your managed or self-hosted server | Instance administrators |
| Workspace | An organisation's or a team's space: members, plan, storage, API tokens, agents | Workspace owners |
| Base | A set of related tables, with its own members and rules | Base owners |
| Table | Fields, views, forms, automations, visibility and row rules | Base owners |
| Record | One row; who sees it can be narrowed by row rules | Contributors (as the rules allow) |
A person can belong to several workspaces with different roles in each.
Deployment models
| Hosted (artavolo.com) | Managed private instance | Self-hosted | |
|---|---|---|---|
| Tenant | Workspace | The whole instance | The whole instance |
| Servers and databases | Shared with other customers | One VM, one database server, one storage bucket and one backup key per customer | Yours |
| Where | OVHcloud, Germany | Hetzner, Germany (Falkenstein or Nuremberg), or another EU location by agreement | Anywhere you run it |
| Isolation | Application level (below) | Physical: nothing shared with other customers | Physical |
| Upgrades | Continuous | Monthly window, announced; snapshot and pre-upgrade backup first | You choose; migrations run automatically |
| Administration | Workspace owners; Artavolo runs the instance | Your administrators through SSO; Artavolo operates the server | Your administrators |
| Licence | Per person, per month | Quoted (Enterprise) | Community (free), or the same per-person plans as hosted with a licence key; Enterprise quoted |
How hosted workspaces are kept apart
On the hosted service tenants share the same database servers. Separation is enforced by the application on every path that hands data out:
- Every request is authenticated and checked against the workspace, base, table and record before any data is read. That covers the browser, the REST and
/v0APIs, AI chat (MCP), file downloads, live updates, exports, guests and published forms. - API tokens are bound to one workspace, and optionally to named bases. A token cannot reach another workspace even when the same person belongs to it.
- Agent identities are scoped to named bases with read or write access, and their changes can be held for review.
- Row rules limit which records a person sees. The server applies them inside the database query.
- Files are kept in a folder per workspace, served through the permission check, and counted against that workspace's storage.
- Live updates go only to people allowed to see the change. A table with a row rule sends live updates only to the people who see every record.
- Every change is in the append-only audit log, with the workspace, base and table it belongs to.
- Optional cell encryption (AES) for instances that want values unreadable in the database itself. It disables server-side filtering on encrypted values.
These boundaries are covered by the automated test suites in tests/, across members, tokens, workspaces and guests.
Using and administering it as an organisation
Roles
| Role | Can |
|---|---|
| Instance administrator | Everything on the instance: members, settings, single sign-on, mail, backups, licence, the whole audit log |
| Workspace owner | Members and invitations, roles, bases, API tokens, agents, the workspace's audit log and export |
| Base owner | Fields, views, forms, automations, table visibility, who may create, edit and delete records, row rules |
| Contributor | Work with records as the base's rules allow |
| Read-only | Read the tables they are allowed to see; keep personal views |
| Guest / form respondent | A published read-only view or a form, optionally password-protected, with no account |
| Agent | A named non-human identity with a scoped token; its changes are credited to it, audited and can be reverted or held for approval |
Identity and access
- Single sign-on with OpenID Connect (Microsoft Entra ID, Okta, Keycloak and any standard OpenID Connect provider). Options:
- just-in-time provisioning into a chosen workspace and role;
- linking existing accounts by verified e-mail;
- mapping e-mail domains to providers;
- enforcing SSO, so passwords work for administrators only.
- SAML 2.0 and LDAP/Active Directory are available on request.
- Two-factor authentication (TOTP with one-time recovery codes) for everyone.
- Sessions: an optional idle timeout, which also revokes "remember me".
- Brute-force lockouts cover passwords, codes and API credentials.
- Leaving: every request is checked against current membership, so removing someone from a workspace also ends their access through API tokens and AI chat. An administrator can block an account outright. Erasure under GDPR Art. 17 removes the account, its tokens, sessions and personal data, and records the erasure in the audit log.
Oversight
- Audit log: append-only. Each entry has the actor, the method (browser, API token, AI chat through which app, agent), the IP, and before/after values. Workspace owners read and export their workspace's entries as CSV or JSON; administrators see everything. Retention is configurable.
- History: record history and comments on every record.
- Recovery:
- deleted records, fields, views and tables stay in a 30-day trash;
- agents' deletes always go to the trash;
- "revert" undoes an agent's batch from the audit values.
- Backups: encrypted with AES-256 and an HMAC tag, covering both databases and the uploads, and checked before any restore. Enterprise adds the daily schedule, retention, off-site copies to S3-compatible storage and failure alerts.
- Health:
GET /statusreports version, database reachability and licence state, with no secrets.
Integrations an IT team uses
- REST data API (
/api/v2): names and ids, filtering, sorting, grouping and paging in SQL. - Airtable-compatible API (
/v0): existing Airtable scripts and SDK code keep working after changing the address and the token. - AI connector (MCP): OAuth sign-in, scoped per connection, every write audited.
- Webhooks: outbound webhooks, and automations that create or update records or call webhooks.
- Embedding: published forms and views can be embedded on any website; signed embeds are restricted to allowed origins.
Limits per tenant
Hosted plans (per workspace)
| Free | Standard | Pro | Enterprise licence (self-hosted) | |
|---|---|---|---|---|
| Records per base | No limit | No limit | No limit | No limit |
| Bases | No limit | No limit | No limit | No limit |
| Members (seats) | No limit | As purchased | As purchased | No limit |
| File storage | 2 GB | 20 GB | 100 GB | 1,000 GB by default; set by the server |
| Base backups kept | 10 | 25 | 50 | No limit |
Storage is the only hosted quota that refuses an action when it is reached: an upload over the remaining space is refused with a clear message. A workspace keeps any larger allowance it was given under an earlier plan. The Community edition on your own server has no application limits.
Limits that protect everyone (all plans)
| What | Limit | Why |
|---|---|---|
| API requests, per token | 600 a minute on the hosted service (Airtable: 5 a second per base) | One script cannot slow down other customers |
| API requests, per workspace | 3,000 a minute on the hosted service | The same, for many tokens at once |
| Over the limit | 429 with Retry-After, in Airtable's error shape on /v0 | Clients that handle Airtable's 429 already back off correctly |
| AI chat changes | 1,000 a day per person (fair use; included in the seat) | |
| One uploaded file | 25 MB (imports 100 MB) | |
| Records per API page | 100 on /v0 (as Airtable); up to 10,000 on /api/v2 | |
| Records per write | 10 on /v0 (as Airtable); 50 per AI chat call | |
| Failed sign-ins | 5 per account or 20 per IP in 15 minutes, then a 15-minute lock | |
| Invitations | 20 an hour per person, 50 per IP |
On a managed or self-hosted instance the API limits are off by default. An administrator can set them in includes/config.php (API_RATE_PER_TOKEN, API_RATE_PER_WORKSPACE, requests a minute; 0 switches a limit off). A single workspace can be given its own figure with the system option api_rate_workspace_<workspace id>. On the hosted service, a workspace that needs more (a nightly sync, a large integration) is raised the same way on request.
How it scales
Large tables
- No row limit. Each table is a real SQL table with typed, indexed columns. Filters, sorts, grouping, counts and search run in the database, never by loading the table into PHP.
- Small tables load whole. The browser holds a table of up to 10,000 records in the page, which makes it instant to filter and scroll.
- Larger tables are paged. Above 10,000 records the grid reads pages from the server as you scroll (SvGrid's server row model), so a table of hundreds of thousands of records opens as quickly as a small one.
- Imports are background jobs. They resume after a failure, back off on Airtable's rate limit, and are tested on a 25,000-record, three-table base.
Growing an instance
| Stage | What to do |
|---|---|
| A team to a few hundred people | One server: application, both databases and files together (the reference Docker stack). Managed instances use 2 vCPU / 4 GB up to 100 users and 4 to 8 vCPU / 8 to 16 GB up to 500 |
| Heavier use | Move the data database to its own server or a managed MySQL/MariaDB or PostgreSQL service (TLS supported). Turn on Redis for caching |
| More application servers | Requests are stateless apart from the PHP session. Run several application servers behind a load balancer, with sticky sessions (or a shared session store) and the uploads directory on shared storage (NFS or similar). Run cron and the websocket server on one node |
| High availability | Database replication with failover (MariaDB/Galera or PostgreSQL streaming replication), two or more application servers, and the encrypted backups shipped off-site daily |
What keeps each request cheap
- Paging. A large table is read a page at a time, with its filters and sorts applied in SQL.
- No waiting on outbound calls. Webhooks and e-mail go out after the response, with 5-second connect and 10-second total timeouts.
- Atomic counters. Rate limits and lockouts are single SQL statements, so they need no Redis on a basic host.
- Live changes as patches. The websocket message carries only the changed cells.
Not there yet
So that a review does not find these first:
- SCIM provisioning. People are created on first SSO sign-in (just in time). Removal is done in Artavolo, not pushed from the identity provider.
- SAML 2.0 and LDAP are available on request, not as a self-serve setting.
- Separate databases per hosted workspace. An organisation that requires physical separation takes a managed private instance.
- A published multi-node reference deployment. The parts are in place (above), but a tested load-balanced compose file and a public benchmark at a million records are still to come.
- A usage view per workspace (API calls, storage over time) for workspace owners. Today storage is shown, and API usage is visible in the audit log.