# Rtdb Security Rules

> Author or audit Realtime Database security rules — cascading read/write access, .validate shape checks, auth expressions, data vs newData semantics. Use when the user works on database.rules.json, asks why an RTDB read/write is allowed or denied, or needs RTDB paths locked down.

- Skill: `davideast/rtdb-security-rules` (Agent Skill)
- Install (CLI): `npx skillmds@latest add davideast/rtdb-security-rules`
- Raw SKILL.md: https://api.skillmd.com/api/skills/davideast/rtdb-security-rules/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: davideast (https://skillmd.com/u/davideast)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/davideast/rtdb-security-rules

---


# RTDB Security Rules

RTDB rules are JSON-embedded expressions where access cascades downward: a
permissive parent grants every descendant, and a restrictive child cannot
revoke it. Lock the root, then open the smallest useful paths.

## Rule types

- `.read` / `.write` — WHO may act at this path (and everything below it).
- `.validate` — WHAT the written data may look like, evaluated only after
  `.write` allows; validations do not cascade.
- `data` — pre-write state (the actor's existing context);
  `newData` — post-write state. In multi-field writes each `.validate` sees
  the full merged `newData`.

## Steps

1. **Read current rules.** From `database.rules.json` in the project, or the
   deployed ruleset read back through the Firebase Console or `firebase-tools`.
   Complete when you can state the effective access at every path a client
   touches (walk each cascade from root).

2. **Identify paths and identities.** List each path clients read or write
   and the identity that should reach it (anonymous, any signed-in user,
   owner via `auth.uid`, role via claim). Complete when each path has an
   intended identity × operation table.

3. **Design access and validation together.** Start from
   `{ "rules": { ".read": false, ".write": false } }` and open exact paths.
   Guard identity with `auth !== null` before `auth.uid` comparisons. Add a
   `.validate` for every user-controlled write: type checks
   (`newData.isString()`, `.isNumber()`), bounds, required children
   (`newData.hasChildren([...])`), and transition checks comparing `data` to
   `newData`. Complete when every open path has both an access rule and a
   shape rule.

4. **Simulate before shipping.** Run `rtdb_simulate_access` for each path
   with four case families: the intended actor allowed, anonymous denied,
   cross-user denied, invalid shape denied. Complete when all four families
   pass per path.

5. **Deploy.** Write the full `database.rules.json`, since a deploy replaces
   the entire ruleset, and ship it with `firebase deploy --only database` or the
   Console. Complete when the deployed rules, read back through the Console or
   `firebase-tools`, match the file, and `rtdb_simulate_access` against a
   local copy of the same file still passes every case from step 4.

## Reference — pitfalls

- A `.read: true` near the root silently exposes every descendant; recheck
  cascades after any parent edit.
- `.validate` never runs when `.write` denies — and never rescues a `.write`
  that is too broad.
- `data` at a path being created is empty; existence checks belong on
  `data.exists()`.
- Deleting a node is a write of `null`: `newData.exists()` in `.validate`
  blocks deletion — decide intentionally.

