How rules work
You define rules as a JSON object that mirrors the shape of your database. At each path, you can attach three rule types:.read— Can this client read data at this path?.write— Can this client write data at this path?.validate— Is the incoming data valid?
.read or .write rule is specified at or above a path, the operation is rejected.
Rules evaluate to true or false. You can use simple booleans or expressions that reference auth state, existing data, and the incoming write.
Cascading rules
.read and .write rules cascade downward. If you grant .read at /users, every child path under /users is also readable. A rule at a parent node overrides any restrictive rule on a child — once access is granted, it can’t be taken away deeper in the tree.
.read: false on /foo/bar has no effect. If /foo/baz is true, the parent rule grants read access to all of /foo — including /foo/bar. Child rules can only grant additional privileges, never revoke them.
.validate rules are the exception. They do not cascade. Every node that has a .validate rule must independently pass validation for a write to succeed. This lets you enforce structure at every level of your data.
Rules are not filters
Rules are evaluated atomically. A read operation fails entirely if there isn’t a rule at that location (or a parent) that grants access — even if every individual child is accessible./records fails with PERMISSION_DENIED, even though rec1 is readable. There’s no .read rule that covers all of /records, so the read is denied. To get rec1, you must read /records/rec1 directly.
This is an important design point: rules don’t filter results down to what’s allowed. They’re all-or-nothing at each path.
Wildcards
Use$wildcard path segments to match any child key. The matched value becomes available as a variable in your rule expressions.
$roomId matches any room key and $messageId matches any message key. You can reference these variables in expressions — for example, "auth.uid === $roomId" to restrict access to the room owner.
Wildcard captures are always strings — even if the key looks like a number. Comparing
$key to a number directly will always fail. Convert the number to a string first: $key === newData.val() + ''.Restricting allowed children
You can use a$other wildcard alongside named children to reject any unexpected fields:
title or color fails validation.
Overlapping statements
When both a$wildcard rule and a specific named rule match a node, both are evaluated. If either condition is false, access is denied.
message1 are denied because the specific message1 rule evaluates to false, even though the $message wildcard rule is true.
Volatile path restrictions
Volatile paths support.read, .write, and .validate rules, but those rules cannot reference data or root — reading existing database state is incompatible with the volatile fast path. If a rule on a volatile path contains data.* or root.*, it evaluates to false. See volatile paths for the full list of what’s allowed.

