feat(localdb): enable replication on the site-a rig pair
Task 11. site-a-a is the initiator (dials scadabridge-site-a-b:8083, the
existing site gRPC h2c listener - no new port); site-a-b is passive. Both carry
the SAME dev ApiKey, which is load-bearing: LocalDbSyncAuthInterceptor is
fail-closed, so a mismatch does not degrade to unauthenticated replication, it
rejects every stream and the pair silently stops converging.
site-b and site-c stay unreplicated on purpose, so the default-OFF posture is
proven side-by-side on one rig rather than asserted.
Plain dev key matches the rig's existing posture; production uses a
${secret:...} reference through the pre-host expander. Noted in both files.
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
@@ -64,6 +64,23 @@
|
||||
// outside the volume and were lost on every recreate.
|
||||
// Replication is opt-in and configured separately; absent = local-only.
|
||||
"LocalDb": {
|
||||
"Path": "/app/data/site-localdb.db"
|
||||
"Path": "/app/data/site-localdb.db",
|
||||
// Site-a is the rig's REPLICATED pair; site-b and site-c deliberately stay
|
||||
// unreplicated so the default-OFF posture is proven side-by-side on one rig.
|
||||
//
|
||||
// This node is the initiator: it dials the peer. Replication is still
|
||||
// bidirectional - the passive node's writes flow back over the same stream -
|
||||
// so only one side needs PeerAddress. Port 8083 is the existing site gRPC
|
||||
// listener (h2c); the sync endpoint shares it, no new port.
|
||||
//
|
||||
// The ApiKey must be IDENTICAL on both nodes: the host's
|
||||
// LocalDbSyncAuthInterceptor is fail-closed, so a mismatch (or a missing key)
|
||||
// rejects every sync stream. A plain dev key here matches the rig's existing
|
||||
// posture; PRODUCTION uses a "${secret:...}" reference resolved by the
|
||||
// pre-host secret expander.
|
||||
"Replication": {
|
||||
"PeerAddress": "http://scadabridge-site-a-b:8083",
|
||||
"ApiKey": "dev-site-a-localdb-sync-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,16 @@
|
||||
// outside the volume and were lost on every recreate.
|
||||
// Replication is opt-in and configured separately; absent = local-only.
|
||||
"LocalDb": {
|
||||
"Path": "/app/data/site-localdb.db"
|
||||
"Path": "/app/data/site-localdb.db",
|
||||
// The PASSIVE half of site-a's replicated pair: no PeerAddress, so this node's
|
||||
// sync initiator starts and idles while node-a dials in. Its own writes still
|
||||
// reach node-a - the stream is bidirectional.
|
||||
//
|
||||
// The key MUST match node-a's exactly. LocalDbSyncAuthInterceptor is
|
||||
// fail-closed, so a typo here does not degrade to unauthenticated replication;
|
||||
// it rejects every stream and the pair silently stops converging.
|
||||
"Replication": {
|
||||
"ApiKey": "dev-site-a-localdb-sync-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user