---
title: "RW-Flag (Remove-Wins Flag, disable-wins)"
url: "https://nsta1.github.io/Orleans.Lattice/docs/crdt/rwflag.html"
source: "https://github.com/NSTA1/Orleans.Lattice/blob/release/9.9/docs/crdt/rwflag.md"
documents: "Orleans.Lattice 9.9.0 (release line 9.9)"
built: "2026-10-04"
all-pages: "https://nsta1.github.io/Orleans.Lattice/llms.txt"
bundle: "https://nsta1.github.io/Orleans.Lattice/docs/crdt/llms-full.txt"
---
# RW-Flag (Remove-Wins Flag, disable-wins)

Part of the [CRDTs documentation](readme.md).

`tree.RwFlag(key)` -> `RwFlagAccessor`, merge mode `LatticeMergeMode.RwFlag`.

## Semantics

An **RW-Flag** is the mirror image of the [OR-Flag](orflag.md): a single boolean
presence bit that converges **disable-wins**. When an enable and a disable race,
the **disable wins** and the flag stays off.

It carries three dot sets: enables, disables, and tombstones that cancel
disables. The flag is on only when at least one enable dot exists **and no
disable dot survives**. A `Disable` an enable has not observed survives the merge
and keeps the flag off, so a revoke is never silently resurrected by a concurrent
re-add.

Both `Enable` and `Disable` take a `replicaId` (each side stamps its own dots).

Use it for: revocation lists, blocklists, "banned" / "deleted" markers, kill
switches - any presence bit where a removal must be the safe, winning outcome.

## Behaviour

Figure: A concurrent RW-Flag enable and disable: disable wins. An order diagram shaped like a diamond. At the bottom the flag is off, with no dots. Cluster A enables it with dot A1, which reads on. Cluster B, at the same time, disables it with dot B1, having observed no enable. Neither state is above the other. Merging keeps both dots, and because no enable has observed and cancelled B1, the join reads off: disable wins.

Both clusters hold enable A1 and disable B1: DISABLED. A surviving disable dot keeps the flag off, so a revoke is never resurrected by a concurrent enable.

The flag is on only while an enable dot exists and no disable dot survives, so the disable wins the race.

```mermaid
sequenceDiagram
    participant A as Cluster A
    participant B as Cluster B
    A->>A: Enable (dot A1)
    B->>B: Disable (dot B1, observed: none)
    A-->>B: merge ships enable A1
    B-->>A: merge ships disable B1
    Note over A,B: B1 was not observed/cancelled by any enable
    Note over A,B: converged = DISABLED (disable-wins)
```

## Example

```csharp verify
var access = tree.RwFlag("user:88:access");

// Cluster A grants access; cluster B concurrently revokes it.
await access.EnableAsync("cluster-A", cancellationToken);
await access.DisableAsync("cluster-B", cancellationToken);

// The revoke was not observed by the grant, so it survives the merge and the
// flag converges DISABLED - a revocation is never lost to a concurrent grant.
bool granted = await access.IsEnabledAsync(cancellationToken);
```

See also: its enable-wins mirror [OR-Flag](orflag.md) and the
[CRDT overview](readme.md).
