Ga naar inhoud
Begin met HitKeep Cloud

HitKeep Teams and Analytics Data Isolation

Deze inhoud is nog niet vertaald.

Multi-tenant analytics platforms typically co-mingle everyone’s data in a single database and rely on row-level filters to keep tenants apart. One bad query, one missing WHERE clause, and data leaks across boundaries. HitKeep takes a different approach: it separates control plane and data plane into distinct databases. Each team’s analytics data lives in its own DuckDB file. There is no shared analytics table to accidentally query across.

HitKeep team switcher showing multiple teams
Team switcher with multiple teams and isolated analytics contexts.
HitKeep create team dialog with name and logo fields
Create a new team directly from the switcher so a fresh analytics context is only one action away.

HitKeep splits its storage into two planes:

Control plane — the shared hitkeep.db:

  • Users, sessions, and authentication (JWT, MFA, passkeys)
  • Sites metadata (domain, settings, team membership)
  • Share links
  • Tenants and tenant membership
  • User preferences (active tenant)

Data plane — per-tenant DuckDB files:

  • Hits (pageviews)
  • Events (custom events and properties)
  • Goals and funnels (canonical configuration plus rollups)
  • Rollups (pre-aggregated daily summaries)

The only reference crossing the boundary is the site_id — the control plane knows which sites exist and which tenant owns them; the data plane stores analytics rows keyed by that site_id. No queries JOIN across the two planes.

HitKeep uses a team (internally called a tenant) as the isolation boundary. Every HitKeep instance has a default team created automatically on first boot. Sites created before teams existed, and sites created by users who never switch teams, belong to this default team.

When you create a second team, HitKeep provisions a separate data plane database for it:

/var/lib/hitkeep/data/
├── hitkeep.db # Control plane + default team data plane
└── tenants/
└── a1b2c3d4-.../
└── hitkeep.db # Team-specific data plane: hits, events, rollups

Every data pathway — ingestion, API reads, background workers, data exports — resolves the correct data plane database before touching analytics data. The resolution is transparent: a site_id is mapped to its owning tenant, and the tenant’s store is returned.

If you run a single-user or single-team instance, nothing changes. The default team’s data plane lives inside the shared hitkeep.db — both planes coexist in one file, no extra databases are created, no configuration required. The isolation layer short-circuits for the default tenant and returns the shared store directly.

Each team has its own role hierarchy, independent of instance-level roles.

Role Permissions
owner Full team control — settings, members, sites, billing
admin Manage members and sites; cannot delete the team
member Access team sites according to their per-site roles

Team owners and admins have effective owner/admin access across the sites in their active team. Team members only see and change sites where they also have an explicit per-site role.

The user who creates a team is automatically assigned the owner role.

HitKeep team administration overview
Overview tab for the active team, including role, team metadata, and usage visibility for sites, members, and monthly tracked events.

HitKeep exposes both current usage and plan entitlements in the team payload so owners and admins can see quota pressure before automation or ingestion is blocked.

GET /api/user/teams includes, for each team:

  • usage.current_sites
  • usage.current_members
  • usage.current_pending_invites
  • usage.current_monthly_events
  • entitlements.max_sites_per_team
  • entitlements.max_team_members
  • entitlements.max_monthly_events

The Team Overview page renders these values as usage cards and progress bars whenever a limit is finite. In the OSS build, the default provider reports unlimited entitlements, so the cards show current usage alongside an unlimited state.

The new team is automatically set as the caller’s active team.

Response includes active_team_id so you know which team context you’re operating in.

After switching, all site operations (create, list, analytics queries) are scoped to the active team.

Owners and admins can invite users by email from the team Members tab. Existing users keep their password, passkeys, remember-me setting, and MFA flow. Their invite email sends them through the normal sign-in page and returns them to the invitation after login.

If the email does not match an existing account, HitKeep creates a placeholder account and sends an invite link. The recipient opens the link, sets a password for that invited email address, and is signed in automatically. Accepting the invite makes the invited team active so the new membership is visible immediately.

Role assignment follows a hierarchy: you can only assign roles at or below your own level.

HitKeep team member management
Owners and admins can manage memberships and role changes from the Members tab.

Site owners and admins can also add a person from a site’s Team settings. If the person is already a member of the site team, HitKeep grants or updates the site role directly.

If the person is not in that team yet, HitKeep creates or reuses a pending team invite first, then records the requested site role. The site role becomes effective after the recipient accepts the team invite. This removes the old two-step workflow where an admin had to invite the user to the team and then return to the site settings page.

Existing site access is idempotent: adding the same user again updates their site role instead of sending another invite.

On HitKeep Cloud, team member limits still apply to both direct team invites and site invites that need a team invite. A pending invite counts toward the team member limit until it is accepted or revoked.

The last remaining owner cannot be removed.

PUT /api/user/teams/{team_id} remains available as a deprecated compatibility alias for one bridge release.

HitKeep team settings tab with team metadata and management controls
The Settings tab centralizes team metadata, ownership-sensitive actions, and shared team-level configuration.

Ownership transfer is an explicit action. The current owner promotes an existing member to owner and is downgraded to admin.

This keeps the “at least one owner” invariant intact and records an audit event for the change.

The same invariant is enforced at the instance-admin level too: deleting a user account is blocked if that user is still the sole owner of any team. Instance admins must transfer ownership first, then delete the user.

Members can leave a team as long as they are not the last remaining owner and the team is not their only team:

Owners can archive an empty team once all sites have been moved or deleted. When the active team is left or archived, HitKeep automatically falls back to the next available team and updates active_team_id.

If you need an irreversible cleanup, an instance owner can permanently delete an already archived team and remove its per-tenant DuckDB file:

This hard-delete path is only available after the team has been archived and all sites have been transferred or deleted.

Instance admins can view and manage all teams from the System Settings → Teams tab. This tab lists every team with its member count, site count, archive status, and creation date.

Any non-default team can be deleted directly from the Teams tab with a single click. HitKeep handles the full cleanup automatically:

  1. Deletes all sites belonging to the team and their analytics data
  2. Archives and purges the team record
  3. Removes the per-tenant DuckDB analytics database

Members keep their instance accounts and remain in any other teams they belong to.

The confirmation dialog warns about the number of sites that will be deleted, so there are no surprises.

Via the API:

Terminal window
# List teams to find the target
curl -s -b cookies.txt https://your-instance/api/admin/teams | jq .
# Delete a team (sites, members, and data are cleaned up automatically)
curl -X DELETE -b cookies.txt https://your-instance/api/admin/teams/{team_id}?force=true

Without ?force=true, the team must already be archived with no sites — useful if you want to handle cleanup manually.

The default team cannot be deleted — it serves as the fallback for users and sites that are not explicitly assigned to a team.

Teams are most useful when you can reorganize existing sites after the fact. HitKeep lets site owners or team managers move a site into another team they administer:

This operation:

  • updates the shared site_tenants mapping in the control plane
  • copies hits, events, goals, funnels, and rollups into the destination tenant database
  • removes stale analytics rows from the previous tenant database
  • appends audit entries in both the source and destination teams

The site_id remains stable, so integrations and existing share links do not need to change.

HitKeep site transfer form inside the site team settings tab
Site settings expose a transfer flow so existing sites can be reorganized between teams without data loss.

Site owners can reset stored stats or delete the site from the site settings Danger Zone. Both flows require typing the site domain before the action is enabled.

Use Reset stats when you want to keep the site shell but remove the measured analytics data currently stored for that site. Reset keeps the site record, team mapping, members, retention policy, exclusions, share links, API client grants, tracking settings, goals, funnels, QR campaigns, Search Console mapping, and import job records. It clears hits, events, Web Vitals, rollups, dirty rollup markers, imported analytics aggregates, Search Console facts, site activity summaries, AI visibility fetches, QR opens, Opportunities, and site-scoped AI run metadata.

Reset does not pause live tracking. Rows written after the reset transaction can appear normally. Completed imports are marked as deleted so their imported aggregate rows do not remain active. Imports that are uploading, validating, queued, or running are left alone; if they write rows after reset, those rows count as new data.

Use Delete site when the site itself should go away. Site deletion removes the site shell and its associated analytics data. Existing API client grants, share links, team access, and configuration tied to that site are cleaned up with the site.

The reset endpoint accepts dashboard sessions only. API client bearer tokens cannot reset stats, even when the token has a site owner grant. See Reset site stats for the API contract.

Every pathway that reads or writes analytics data resolves the tenant’s data plane database first. No analytics query ever touches the control plane, and no control plane query ever touches analytics data.

Pathway Plane What happens
Ingestion (hits & events via NSQ) Data Consumer resolves site_idtenant_id → tenant DB, then writes
AI fetch ingest Data Authenticated handler resolves the site tenant and writes crawler records directly to the tenant DB
API reads (stats, hits, events, timeseries) Data Handler resolves tenant DB before running analytics queries
MCP reads Data Leader-only MCP tools use API client permissions and the same tenant store resolution as REST analytics reads
Workers (retention, rollups, reports) Data Each site’s tenant DB is resolved before pruning, aggregating, or exporting
Exports (CSV, Parquet, XLSX, JSON, NDJSON) Data COPY queries execute against the tenant-scoped database
Site CRUD (create, list, delete, settings) Control Operates on the shared hitkeep.db
Goals & Funnels (create, update, delete, timeseries) Data Canonical rows live in the tenant DB; shared legacy copies remain only for bridge-release rollback safety
Share links (create, revoke) Control Link metadata in shared DB; shared dashboard reads go to data plane

This release keeps legacy shared goals and funnels tables only as a rollback bridge.

  • Reads prefer the tenant-local data plane.
  • If a non-default team site has legacy shared goals or funnels but none in its tenant DB yet, HitKeep backfills them automatically on first access.
  • Writes and deletes are mirrored into the shared legacy tables for one release so rollbacks remain possible.
HitKeep team audit log
Team activity is recorded in the audit log so member and settings changes remain traceable.

The control plane and each team’s data plane are independent DuckDB files. Back up the entire data directory to capture everything:

Terminal window
# Back up control plane + all data planes
cp -r /var/lib/hitkeep/data/ /backup/hitkeep-$(date +%Y%m%d)/

Or back up a specific team’s data plane separately:

Terminal window
# Back up one team's analytics data
cp /var/lib/hitkeep/data/tenants/{team_id}/hitkeep.db /backup/team-{team_id}.db

The control plane (hitkeep.db at the root) must always be included in backups — it contains user accounts, site definitions, and tenant membership required to make the data plane files meaningful.

HitKeep Cloud provisions managed tenants in your chosen region (EU Frankfurt or US Virginia) with encrypted backups and region-aware hosting. Start with HitKeep Cloud →