> ## Documentation Index
> Fetch the complete documentation index at: https://docs.larksh.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Security rules

> Control who can read and write your data

Security rules are the gatekeepers of your Lark database. They're declarative JSON expressions that live alongside your data structure, and they're evaluated entirely on the server. No matter what a client sends, the rules decide whether the operation goes through.

## 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?

Access is **denied by default**. If no `.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.

```json theme={null}
{
  "rules": {
    "players": {
      "$playerId": {
        ".read": true,
        ".write": "auth.uid === $playerId"
      }
    }
  }
}
```

In this example, anyone can read any player's data, but only the player themselves can write to their own node.

## 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.

```json theme={null}
{
  "rules": {
    "foo": {
      ".read": "data.child('baz').val() === true",
      "bar": {
        ".read": false
      }
    }
  }
}
```

Here, the `.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.

<Warning>
  Be intentional about where you place `.read` and `.write` rules. A permissive rule high in the tree grants access to everything below it.
</Warning>

`.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.

```json theme={null}
{
  "rules": {
    "records": {
      "rec1": { ".read": true },
      "rec2": { ".read": false }
    }
  }
}
```

Reading `/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.

```json theme={null}
{
  "rules": {
    "rooms": {
      "$roomId": {
        ".read": true,
        "messages": {
          "$messageId": {
            ".write": "auth !== null",
            ".validate": "newData.hasChildren(['text', 'author'])"
          }
        }
      }
    }
  }
}
```

Here, `$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.

<Note>
  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() + ''`.
</Note>

### Restricting allowed children

You can use a `$other` wildcard alongside named children to reject any unexpected fields:

```json theme={null}
{
  "rules": {
    "widget": {
      "title": { ".validate": true },
      "color": { ".validate": true },
      "$other": { ".validate": false }
    }
  }
}
```

Any write that includes a child other than `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.

```json theme={null}
{
  "rules": {
    "messages": {
      "$message": {
        ".read": true,
        ".write": true
      },
      "message1": {
        ".read": false,
        ".write": false
      }
    }
  }
}
```

Reads to `message1` are denied because the specific `message1` rule evaluates to `false`, even though the `$message` wildcard rule is `true`.

## Volatile path restrictions

[Volatile paths](/platform/volatile) 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](/platform/volatile#rules-restrictions-on-volatile-paths) for the full list of what's allowed.

## Editing rules

You edit your security rules in the Lark dashboard. Go to **Project Settings > Security Rules**, paste or edit your JSON, and click **Publish**. Rules take effect immediately across all connected clients.

<Tip>
  Start with restrictive rules and open access only where you need it.
</Tip>

## What's next

<CardGroup cols={2}>
  <Card title="Rules reference" icon="book" href="/platform/rules-reference">
    Full reference for every variable, method, and operator available in rule expressions.
  </Card>

  <Card title="Rules examples" icon="code" href="/platform/rules-examples">
    Copy-paste patterns for common scenarios: user-owned data, role-based access, write-once data, and more.
  </Card>
</CardGroup>
