| … | … | @@ -148,6 +148,72 @@ A bridge holds its own credentials — ap-bridge carries an RSA keypair for HTTP |
| 148 | 148 | atproto path uses the shared `#atproto` signing key. Those are the **bridge's** secrets, not a |
| 149 | 149 | person's. So a bridge is a resource with a credential set, never a row in `identities[]`. |
| 150 | 150 | |
| 151 | ### 6.1.1 Signing custody — the asymmetry, and what it decides |
| 152 | |
| 153 | **atproto's signature is over CONTENT; ActivityPub's is over the TRANSPORT.** That one difference |
| 154 | decides every custody question below, and `ap-bridge/lib/signer.js`'s header states it: |
| 155 | |
| 156 | > *"atproto-bridge achieves [keylessness] because a peer's signature travels in its firehose, so the |
| 157 | > membrane re-serves a signature it never produced. ActivityPub has no equivalent — HTTP Signatures |
| 158 | > are computed per-request over live request headers, so something must sign at delivery time."* |
| 159 | |
| 160 | ⇒ **A thin signer for `atproto-bridge` makes no sense** — it is already keyless *for free* (verified: |
| 161 | `RepoManager`, `signCommit`, `privateKey` appear only under `pds/test/`). ⇒ **ap-bridge must hold a |
| 162 | key to deliver at all**, today as `LocalSigner`. |
| 163 | |
| 164 | **Is ap-bridge a security issue? Yes, bounded — and the code says so:** *"a compromised bridge yields |
| 165 | the actor's identity; a RemoteSigner means it yields nothing but the ability to ask for signatures |
| 166 | while the connection lasts."* |
| 167 | |
| 168 | | a stolen AP RSA key **can** | **cannot** | |
| 169 | |---|---| |
| 170 | | deliver forged activities to any fediverse inbox — post, delete, follow **as** the actor | touch canonical records (they are in rooms) | |
| 171 | | | forge atproto records (different key) | |
| 172 | | | write to any room (not an admitted writer) | |
| 173 | |
| 174 | ★ **This is precisely what §4's demotion buys.** Before it, compromising the peer yielded *the |
| 175 | identity*; now it yields *one network's delivery capability* — a **grant to revoke and a key to |
| 176 | rotate**, not an identity to mourn. |
| 177 | |
| 178 | ⚠ **`RemoteSigner` only pays when the trust boundary is real.** The seam exists and is unwired |
| 179 | (hyper-social #29 M2; the header says swapping is "one line of wiring"), and it costs **one RPC per |
| 180 | delivery** — unbatchable, because the signature covers `host` and `(request-target)`, so a fan-out to |
| 181 | N inboxes is N signatures. A RemoteSigner from a bridge to a peer **on the same box buys nothing**: |
| 182 | compromising that box gets you the peer anyway. So it is a *deployment* decision — co-located → |
| 183 | `LocalSigner`; shared or remote hardware → `RemoteSigner`. |
| 184 | |
| 185 | ### 6.1.2 ★ Signing is a SERVICE, not a device capability |
| 186 | |
| 187 | The DID `#atproto` key is currently **copied to every co-owner device**. That is the v1-DeviceLinker |
| 188 | defect one layer up: **N holders, and you cannot revoke one.** |
| 189 | |
| 190 | The fix falls out of §6.3 rather than being new. The DID key belongs to `bridge:bsky`, so run **one |
| 191 | instance per identity**, co-located with the always-on peer. Devices write records to the room as |
| 192 | attested writers; the bridge materialises and signs. |
| 193 | |
| 194 | | requirement | met by | |
| 195 | |---|---| |
| 196 | | the key stays private | **one** holder, not N | |
| 197 | | multi-device | writing and signing are **different capabilities** — a new device gets a writer attestation, never the DID key | |
| 198 | | keys out of the membrane | already true; unchanged | |
| 199 | |
| 200 | ★ **It deletes the signer election.** `atproto-room`'s `signer-presence.js` (105), |
| 201 | `signer-presence-channel.js` (57) and `lease.js` (90) — **~252 lines** — exist solely to arbitrate |
| 202 | among N devices that *could* sign. With one signer there is nothing to elect. |
| 203 | |
| 204 | **The cost, and it is correct rather than merely tolerable:** if the bridge is offline the bsky |
| 205 | projection stalls, while the canonical record still replicates to every device. **A peripheral being |
| 206 | down degrades the peripheral** — which is the whole claim of `SOCIAL-MODEL.md`. |
| 207 | |
| 208 | ⛔ **The residual, and it is not solved: the PLC rotation key.** If the bridge box dies the DID |
| 209 | signing key dies with it, PLC is append-only so the DID survives *frozen*, and recovery is **only** |
| 210 | via the rotation key — a different secret with different backup requirements. That is hyper-social |
| 211 | #48's scar (rotation authority recovered from a backup) and #49. **Nothing here addresses it.** |
| 212 | |
| 213 | ⚠ **Second, smaller:** a co-owned community room has N co-owners but one `bridge:bsky`, so *who runs |
| 214 | the community's bridge* becomes a governance question. Coherent — ap-bridge is already single-instance |
| 215 | and `SOCIAL-MODEL.md` §6.7 makes governance a room role — but it should be decided, not defaulted. |
| 216 | |
| 151 | 217 | ### 6.2 `reach` resolves through the bridge registry |
| 152 | 218 | |
| 153 | 219 | `reach` in a record names a **network** (`design.md` §5.1). The bridge registry resolves |