# Resilience (a backup copy) Source: https://selfstore.dev/docs/resilience Keep a second synced copy of the backup on another destination - same bytes, same key, written after every save. A revoked token or a deleted bucket then costs you nothing. One home is a single point of failure: a token gets revoked, a bucket gets deleted, a provider goes down. A **backup copy** (a replica) is a second home that holds the same encrypted backup, rewritten after every save or converge that moved data. It is additive and opt-in - it attaches after your primary home and never changes the primary flow. ## In the backups widget If you mount [``](https://selfstore.dev/docs/widgets), the whole journey is one property. Build a `replicaFlow` with the destinations you want to offer (the same `targets` spec as the connect flow) and assign it: ```ts import { replicaFlow } from 'selfstore/flows'; el.replica = replicaFlow(store, { drive: gisDriveAuth({ clientId }), // a second file on the same account file: true, webdav: true, // 'true' renders a config form in the panel s3: true, }); ``` A "Backup copy" entry appears in the active backup's menu; picking a destination attaches the copy and its status card renders under the active card, with its own Remove. The flow remembers the chosen destination (under a scoped `replica:` prefix, isolated from the primary's session) and re-attaches it silently at every boot - the widget calls `restore()` for you. Without the property, the panel is unchanged. The copy's file is named after the primary with a `.replica` suffix ("App.zip" becomes "App.replica.zip"), so a copy on the same Drive account never collides with the primary or shows up as a backup of its own. ## From the API No widget needed - the simple store carries the same feature as two calls. Build a target for the second home the same way any target is built, then: ```ts import { s3Target } from 'selfstore/advanced'; // Primary home: Google Drive (connected on the simple store as usual). await store.connectDrive(gisDriveAuth({ clientId })); // Resilience copy: an S3 bucket, written alongside every save. const replica = await s3Target.connect({ kv: store.flowHost.kv, config: { endpoint, region, bucket, key, accessKeyId, secretAccessKey }, }); const id = store.addReplica(replica); // ...later: store.removeReplica(id); ``` `store.advanced.state.replicas` lists what is attached and each one's last result. At this level the attachment is memory-only: your app re-attaches at boot (the credential is restored from the cache KV; the attach is one call). `replicaFlow` is exactly that wiring, packaged - use it headless if you want the persistence without the widget. ## What it is, and is not - **Same bytes, same key.** A replica receives the exact archive your primary home does, under the store's own key - no second password, no extra Argon2id pass. It is a copy for availability, not a re-encryption. - **It never gates the store.** A replica that is offline or rejects a write records its error on `state.replicas` and is retried; it cannot block a save or hold your primary home hostage. - **It is resilience, not sharing.** Because it carries the same key, a replica is another copy *you* can restore from - not a copy addressed to someone else. For sharing between people, see [peers and groups](https://selfstore.dev/docs/peers). ## The honest note A replica multiplies the number of operators who hold your data. When the backup is encrypted, they each hold only ciphertext, so N homes disclose no more than one - the encryption is what makes fanning out safe. With no password set, a replica copies plaintext to one more operator, which is exactly when [`requireEncryption`](https://selfstore.dev/docs/sensitive) earns its keep. This is threat **T12** in the [threat model](https://github.com/selfstoredev/selfstore/blob/main/THREAT-MODEL.md). Map of this site for a model: https://selfstore.dev/llms.txt Every page in one file: https://selfstore.dev/llms-full.txt