rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /messages/{messageId} { // anyone can read messages allow read: if true; // anyone can create a message // text must exist and be under 240 characters allow create: if request.resource.data.text is string && request.resource.data.text.size() <= 240; // the only update allowed is an echo // echoCount and lastEchoAt fields can be changed // this prevents anyone from editing the text of someone else's message allow update: if request.resource.data.diff(resource.data) .affectedKeys().hasOnly(['echoCount', 'lastEchoAt']); // nobody can delete messages through the client allow delete: if false; } // --- "passport stamps" -------------------------------------------------- // each anonymous-auth user has their own users/{uid}/stamps/{geohash4} // subcollection (see firebase/stamps.js) - request.auth.uid == userId // means a device can only ever read/write its OWN stamps, never anyone // else's. Stamps are permanent once earned: no update/delete from the // client, and create is validated the same way the messages create rule // above validates its fields. match /users/{userId}/stamps/{stampId} { allow read: if request.auth != null && request.auth.uid == userId; allow create: if request.auth != null && request.auth.uid == userId && request.resource.data.keys().hasAll(['geohash4', 'city', 'country', 'iconId', 'color', 'earnedAt']) && request.resource.data.geohash4 is string && request.resource.data.city is string && request.resource.data.country is string; allow update: if false; allow delete: if false; } // --- global "memory counter" (GlobalCountPill) ------------------------- // a single shared doc, meta/stats, holding totalMessagesEverPosted. // anyone can read it (it's shown to every visitor); writes are limited to // just that one field, same "only these fields can change" pattern as the // echoCount update rule above, so this doc can't be hijacked to store // arbitrary data. match /meta/stats { allow read: if true; // first-ever write: only the counter field, and it must be a number allow create: if request.resource.data.keys().hasOnly(['totalMessagesEverPosted']) && request.resource.data.totalMessagesEverPosted is number; // every later write (increment(1)) may only touch that same field allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(['totalMessagesEverPosted']); // nobody can delete the counter through the client allow delete: if false; } } }