owlette docs
self-hosting

firestore security rules

Firestore security rules control who can read and write data. owlette's rules enforce site-scoped access, agent isolation, and role-based permissions.


deployment methods

# Install Firebase CLI
npm install -g firebase-tools

# Login and select project
firebase login
firebase use --add

# Deploy rules
firebase deploy --only firestore:rules

Expected output:

✔ firestore: rules file firestore.rules compiled successfully
✔ firestore: released rules firestore.rules to cloud.firestore
✔ Deploy complete!

method 2: firebase console

  1. Go to Firebase Console → Firestore Database → Rules tab
  2. Copy contents of firestore.rules from the repository
  3. Paste into the editor
  4. Click "Validate" (should show green checkmark)
  5. Click "Publish"
  6. Rules are live within ~30 seconds

Source of truth

The firestore.rules file in the repository is the canonical version. Always deploy from the file to keep version control synchronized.


rule architecture

key functions

functionpurpose
isAuthenticated()User has a valid Firebase Auth token
isSuperadmin()User document has role: "superadmin"
isServiceAccount()Request token has admin: true; Firebase Admin SDK calls bypass rules entirely
isAgent()Token has role: "agent", site_id, and machine_id claims
isSiteOwner(siteId)Site document owner matches the authenticated user id
agentCanAccessSite(siteId)Agent token site_id matches siteId
agentCanAccessMachine(siteId, machineId)Agent token matches both site_id and machine_id
canAccessSite(siteId)User owns the site, is a superadmin, or has siteId in their user document sites[] array
isSiteAdmin(siteId)User has role: "admin" or "superadmin" and passes canAccessSite(siteId)
hasRequiredFields(required)Write payload contains all required field names

access matrix

These are Firestore client-rule permissions. Trusted server routes that use the Firebase Admin SDK bypass these rules; paths marked "server/Admin SDK only" are denied to ordinary client SDK writes and must be managed by server code.

pathmember/admin clientsuperadmin clientagent clienttrusted server / Admin SDK
sites/{siteId}Read owned or assigned sites; no direct writeRead all sites; no direct writeNo accessCreate/update/delete
sites/{siteId}/machines/{machineId}Read machines in accessible sites; no direct writeRead all machines; no direct writeRead/write/delete own machine onlyFull write
sites/{siteId}/machines/{machineId}/commands/{commandDoc}Read commands in accessible sites; no direct writeRead all commands; no direct writeRead/write/delete own machine commandsFull write
sites/{siteId}/machines/{machineId}/screenshots/{screenshotId}Read screenshots in accessible sitesRead all screenshotsNo accessWrite history entries
sites/{siteId}/machines/{machineId}/installed_software/{softwareId}Read and delete inventory in accessible sitesRead and delete inventory in all sitesRead/write/delete own machine inventoryFull write
sites/{siteId}/machines/{machineId}/hardware/{docId}Read hardware in accessible sites; no direct writeRead all hardware; no direct writeRead/write/delete own machine hardwareFull write
sites/{siteId}/machines/{machineId}/metrics_history/{bucketId}Read metrics in accessible sites; no direct writeRead all metrics; no direct writeRead/write/delete own machine metricsFull write
sites/{siteId}/deployments/{deploymentId}Read deployments in accessible sites; no direct writeRead all deployments; no direct writeNo accessCreate/update/delete
sites/{siteId}/installer_templates/{templateId}Read templates in accessible sites; no direct writeRead all templates; no direct writeNo accessCreate/update/delete
sites/{siteId}/project_templates/{templateId}Read templates in accessible sites; no direct writeRead all templates; no direct writeNo accessCreate/update/delete
sites/{siteId}/project_distributions/{distributionId}Read legacy distributions in accessible sites; no direct writeRead all legacy distributions; no direct writeNo accessCreate/update/delete
sites/{siteId}/roosts/{roostId}Read/create/update/delete roost shells in accessible sites; client creates cannot set version pointers and client updates cannot change version pointers or schemaVersionSame for all sitesNo accessPublish/rollback version pointer changes
sites/{siteId}/roosts/{roostId}/target_state/{machineId}Read/delete target state in accessible sites; no direct create/updateRead/delete all target state; no direct create/updateCreate/update own machine target stateFull write
sites/{siteId}/roosts/{roostId}/versions/{versionId}Read version history in accessible sites; no direct writeRead all version history; no direct writeNo accessServer/Admin SDK only
sites/{siteId}/webhooks/{webhookId}Read webhooks in accessible sites; no direct writeRead all webhooks; no direct writeNo accessWrite
sites/{siteId}/logs/{logId}Read logs in accessible sites; no direct writeRead all logs; no direct writeRead site logs and create own-machine log entriesCreate/delete
sites/{siteId}/audit_log/{entryId}Site admins can read; members cannot readRead all audit entriesNo accessServer/Admin SDK only
sites/{siteId}/settings/{settingId}Read settings in accessible sites; no direct writeRead all settings; no direct writeNo accessWrite
config/{siteId}/machines/{machineId}Read machine config in accessible sites; no direct writeRead all machine config; no direct writeRead/write/delete own machine configFull write
config/{siteId}/schedule_presets/{presetId}Read presets in accessible sites; no direct writeRead all presets; no direct writeNo accessWrite/delete
config/{siteId}/reboot_presets/{presetId}Read presets in accessible sites; no direct writeRead all presets; no direct writeNo accessWrite/delete
config/{siteId}/project_distribution_presets/{presetId}Read presets in accessible sites; no direct writeRead all presets; no direct writeNo accessWrite/delete
users/{userId}Read own profile, self-create as member, and update allowed self fields without changing role, email, or sitesRead all user profiles; role/site writes still server-mediatedNo accessManage roles, site assignments, and deletion
users/{userId}/settings/{settingId}Read/write own settings onlyRead/write own settings onlyNo accessAdmin SDK bypass only
users/{userId}/devicePrefs/{docId}Read/write own device preferences onlyRead/write own device preferences onlyNo accessAdmin SDK bypass only
users/{userId}/api_keys/{keyId}Read own API key inventory; no direct writeRead own API key inventory only; no direct writeNo accessServer/Admin SDK only
installer_metadata/{document=**}Public readPublic readPublic readWrite
system_presets/{presetId}Read if authenticated; no direct writeRead if authenticated; no direct writeRead if authenticated; no direct writeCreate/update/delete
chats/{chatId} and chats/{chatId}/messages/{messageId}Create own or autonomous chats; read/write own chats and messages; read autonomous chats for accessible sitesSame, plus read autonomous chats for all sitesNo site-scoped reads; generic authenticated create/owner rules still applyAdmin SDK bypass only
agent_tokens/{tokenId}No accessNo accessNo accessServer/Admin SDK only
agent_refresh_tokens/{tokenHash}No accessNo accessNo accessServer/Admin SDK only
device_codes/{phrase}No accessNo accessNo accessServer/Admin SDK only
api_keys/{keyHash}No accessNo accessNo accessServer/Admin SDK only
any unmatched pathNo accessNo accessNo accessAdmin SDK bypass only

agent authentication

Agents use custom Firebase tokens with claims:

{
  "role": "agent",
  "site_id": "nyc-office",
  "machine_id": "DESKTOP-ABC123"
}

Rules enforce strict machine isolation for machine-scoped agent writes. An agent can write only its own machine documents, config, command documents, inventory, hardware, metrics, logs, and roost target state. Agents can also read their own machine state, site logs, and authenticated system presets where the matrix above allows it.


testing rules

rules playground

  1. Firebase Console → Firestore → Rules → Playground
  2. Set the document path to test
  3. Configure authentication (user, admin, or agent claims)
  4. Test read/write operations

example tests

Agent accessing own machine (should allow):

  • Path: sites/site_abc/machines/DESKTOP-001
  • Auth: Custom claims {role: "agent", site_id: "site_abc", machine_id: "DESKTOP-001"}
  • Operation: GET → Should be allowed

Agent accessing different machine (should deny):

  • Same as above but machine_id: "DESKTOP-002"
  • Operation: GET → Should be denied

Token collection access (should deny everyone):

  • Path: agent_tokens/test_code
  • Auth: Any (even admin)
  • Operation: GET → Should be denied

rollback

If rules cause issues:

via firebase console

  1. Firestore → Rules → Click "History" (clock icon)
  2. Find previous version → Click "Restore"

via git

git checkout HEAD~1 firestore.rules
firebase deploy --only firestore:rules

versioning

Firestore rules version is managed independently from the product version. The rules version is tracked inside the firestore.rules file header comment.


monitoring

  • Firebase Console → Firestore → Usage: Monitor denied reads/writes
  • Spike in denials after a rule change = something broke
  • Agent logs: Look for "Permission denied" or "HTTP error 403"
  • Browser console: Look for "Missing or insufficient permissions"

on this page