Un server di sincronizzazione tuoA sync server of your own
Divano sincronizza i dispositivi di casa attraverso una piccola API HTTP, descritta qui in OpenAPI 3.1. Il server conserva solo dati cifrati sui dispositivi: chiunque può ospitarne uno, e l'app ha bisogno solo del suo indirizzo.
Divano syncs the devices of a household through a small HTTP API, described here in OpenAPI 3.1. The server only keeps data encrypted on the devices: anyone can host one, and the app only needs its address.
Come funziona
- Uno spazio è l'insieme dei dispositivi di una famiglia. Ha una chiave che conoscono solo i suoi dispositivi e che il server non riceve mai.
- Il server conserva record
{id, rev, deleted, blob}per spazio.idè un'impronta calcolata con la chiave eblobè cifrato con AES-256-GCM: il server sa quanti record ci sono, quanto sono grandi (arrotondati) e quando cambiano, non cosa contengono. revè un contatore per spazio assegnato dal server. Le scritture sono compare-and-set surev: l'ordine del server fa fede e l'orologio dei dispositivi non conta.- Un nuovo dispositivo entra nello spazio con un abbinamento: mostra un codice QR, un dispositivo già presente lo inquadra, controlla l'impronta della chiave e gli passa la chiave dello spazio cifrata per lui. Il server la custodisce senza poterla aprire.
Un server malevolo può non rispondere o restituire dati vecchi, ma non può leggere né falsificare i record, né far entrare un suo dispositivo in uno spazio. Il formato dei record e la crittografia riguardano solo l'app: il server deve soltanto conservarli e restituirli in ordine.
How it works
- A space is the set of devices of one household. It has a key that only its devices know and that the server never receives.
- The server stores records
{id, rev, deleted, blob}per space.idis a keyed hash andblobis encrypted with AES-256-GCM: the server learns how many records there are, how big they are (rounded) and when they change, not what they contain. revis a per-space counter assigned by the server. Writes are compare-and-set onrev: the server's order is the truth and no device clock matters.- A new device joins a space by pairing: it shows a QR code, a device already in the space scans it, checks the key fingerprint and hands over the space key encrypted for it. The server holds it without being able to open it.
A malicious server can refuse service or hand back stale data, but it cannot read or forge records, nor slip a device of its own into a space. The record format and the cryptography only concern the app: the server just stores records and hands them back in order.
Cosa deve fare un server
- Rispondere su HTTPS. Se sta dietro un proxy, il timeout di lettura del proxy deve superare la durata dei long-poll (
maxWaitSeconds). - Servire
GET /discoverysenza autenticazione: versioni del protocollo, regole di registrazione, limiti, funzioni e chi gestisce il server. Non sotto/.well-known, che alcuni hosting riservano a sé. - Salvare i record in una tabella
records(space_id, id, rev, deleted, blob)con chiave(space_id, id)e un indice su(space_id, rev), più un contatore per spazio. - Eseguire ogni push in una transazione: controllare ogni
baseRev, assegnarerev = ++contatorealle scritture applicate, restituire il record attuale per i conflitti. - Dimenticare i record cancellati dopo
tombstoneRetentionDays(almeno 30) e ricordare ilrevpiù alto eliminato: un pull consincepiù vecchio riceve 410cursor_expired. - Long-poll: svegliare i pull in attesa quando il contatore dello spazio cambia. Chi non può tenere aperte le richieste annuncia
maxWaitSeconds: 0e toglielongpolldafeatures. - Far scadere gli abbinamenti dopo 10 minuti; ogni codice si usa una volta sola.
- Conservare dei token solo l'impronta SHA-256 e non registrare mai i corpi delle richieste né i token.
- Limitare per IP
POST /v1/spacesePOST /v1/pairings. - Per un server privato, usare
registration: secret: chi crea uno spazio deve conoscere la password del server.
What a server must do
- Answer over HTTPS. Behind a proxy, the proxy's read timeout must be longer than the long-polls (
maxWaitSeconds). - Serve
GET /discoverywithout authentication: protocol versions, registration rules, limits, features and who runs the server. Not under/.well-known, which some hosts reserve for themselves. - Store records in a table
records(space_id, id, rev, deleted, blob)keyed on(space_id, id)with an index on(space_id, rev), plus a per-space counter. - Run each push in one transaction: check every
baseRev, assignrev = ++counterto the applied writes, return the current record for conflicts. - Forget deleted records after
tombstoneRetentionDays(at least 30) and remember the highest purgedrev: a pull with an oldersincegets 410cursor_expired. - Long-poll: wake waiting pulls when the space's counter moves. A server that can't hold requests open advertises
maxWaitSeconds: 0and dropslongpollfromfeatures. - Expire pairings after 10 minutes; each code is single use.
- Keep only a SHA-256 hash of tokens and never log request bodies or tokens.
- Rate-limit
POST /v1/spacesandPOST /v1/pairingsper IP. - For a private server, use
registration: secret: whoever creates a space must know the server password.
Per esempio, il server ufficiale risponde così aFor example, the official server answers GET https://sync-divano.dambox.ch/discovery:
{
"protocols": [1],
"registration": "open",
"features": ["longpoll"],
"limits": {
"maxRecords": 20000, "maxBytes": 33554432, "maxBlobBytes": 16384,
"maxBatch": 500, "maxDevices": 10, "maxWaitSeconds": 30,
"tombstoneRetentionDays": 90, "inactiveSpaceDays": 365
},
"operator": {
"name": "Divano",
"privacyUrl": "https://divano.dambox.ch/privacy/",
"contact": "app+divano@dambox.ch"
}
}
Collegare l'app
- In Impostazioni › Sincronizzazione, prima di iniziare, scrivi l'indirizzo del server nel campo Server. Senza schema, Divano aggiunge
https://;http://è accettato solo per indirizzi della rete di casa (localhost,.local,.lan,.home.arpa, IP privati). - Con
registration: secretcompare Password del server: è il segreto che hai configurato. - Usa lo stesso server su tutti i dispositivi: durante l'abbinamento l'app controlla che coincida.
Connecting the app
- In Settings › Sync, before you start, type the server address in the Server field. Without a scheme Divano adds
https://;http://is only accepted for home network addresses (localhost,.local,.lan,.home.arpa, private IPs). - With
registration: secret, Server password appears: it's the secret you configured. - Use the same server on every device: pairing checks that they match.
Riferimento APIAPI reference
Generato da openapi.yaml, in inglese come la specifica. Corpi JSON, errori application/problem+json (RFC 9457), valori binari in base64url senza padding, orari in millisecondi Unix.
Generated from openapi.yaml. JSON bodies, application/problem+json errors (RFC 9457), binary values in base64url without padding, times in Unix milliseconds.