> For the complete documentation index, see [llms.txt](https://astroport-1.gitbook.io/astroport.one/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://astroport-1.gitbook.io/astroport.one/docs/reference/roaming_udrive_sync.md).

# Roaming uDRIVE Sync via NIP-04 DMs

Canal de communication inter-NODE pour synchroniser les fichiers uDRIVE d'un utilisateur MULTIPASS en itinérance vers sa home station.

## Principe

Quand un MULTIPASS se connecte à une station visiteur (B), il peut uploader des fichiers via UPassport. Ces fichiers doivent rejoindre son uDRIVE sur sa home station (A), qui seule a autorité pour publier l'IPNS.

```
Station B (visiteur) :
  1. NIP-42 auth → 22242.sh crée répertoire éphémère + .roaming flag
                   + sauvegarde NOSTRNS et G1PUBNOSTR depuis swarm data

  2. Upload → /api/fileupload
        → ipfs add (via upload2ipfs.sh) → CID
        → _maybe_send_roaming_dm() : DM NIP-04 immédiat vers home station NODE HEX
        → supprime le fichier local si DM réussi
        → retourne file_cid au client (pas de régénération uDRIVE locale)

  3. NOSTRCARD.refresh.sh CLEANUP (chaque cycle)
        Supprime les répertoires roaming sans marker NIP-42 depuis 24h

Station A (home) :
  NOSTRCARD.refresh.sh DM listener (en tête de chaque cycle)
  → nostr_node_intercom.py receive --channel udrive --since LAST_TS
  → ipfs get CID → APP/uDRIVE/[type]/[filename]
  → touch uDRIVE/ → should_refresh() détecte le changement
  → generate_ipfs_structure.sh + ipfs name publish (IPNS)
```

## Script : `tools/nostr_node_intercom.py`

Canal générique de communication inter-NODE via DMs NIP-04 (kind 4). Chaque message porte un champ `channel` identifiant le sous-protocole.

### Envoyer un fichier uDRIVE (Station B → Station A)

```bash
python3 tools/nostr_node_intercom.py send-udrive \
    --nsec    "$NODE_NSEC_B" \
    --to      "$HOME_NODE_HEX" \
    --email   "user@example.com" \
    --cid     "QmXxx..." \
    --filename "photo.jpg" \
    --filetype image \
    --relays  wss://relay.copylaradio.com
```

**Paramètres `--filetype`** : `image`, `video`, `audio`, `document`, `file`

### Recevoir les demandes de sync (Station A — home)

```bash
python3 tools/nostr_node_intercom.py receive \
    --nsec    "$NODE_NSEC_A" \
    --channel udrive \
    --since   "$LAST_TS" \
    --relays  wss://relay.copylaradio.com
```

**Sortie JSON** :

```json
[
  {
    "channel": "udrive",
    "payload": {
      "email":    "user@example.com",
      "cid":      "QmXxx...",
      "filename": "photo.jpg",
      "filetype": "image"
    },
    "event_id":   "abc123...",
    "sender":     "deadbeef...",
    "created_at": 1700000000
  }
]
```

### Envoyer un message générique

```bash
python3 tools/nostr_node_intercom.py send \
    --nsec    "$NODE_NSEC" \
    --to      "$DEST_HEX" \
    --channel "my_channel" \
    --payload '{"key": "value"}' \
    --relays  wss://relay.copylaradio.com
```

## Canaux définis

| Canal      | Payload requis                                                                                                                                                          | Usage                                                                              |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `udrive`   | `email`, `cid`, `filename`, `filetype`                                                                                                                                  | Sync fichier uDRIVE visiteur → home                                                |
| `vocals`   | `email`, `cid`, `filename`, `filetype`, `mime_type`, `duration`, `title`, `kind`                                                                                        | Publication kind 1222/1244 (vocal) via home                                        |
| `webcam`   | `email`, `cid`, `filename`, `filetype`, `mime_type`, `duration`, `title`                                                                                                | Publication kind 21/22 (vidéo) via home                                            |
| `bro_ia`   | `pubkey`, `event_id`, `lat`, `lon`, `message`, `url`, `kname`                                                                                                           | Commande BRO (#BRO kind 1) relayée depuis visiteur                                 |
| `zen_like` | `email`, `sender_pubkey`, `event_id`, `reacted_event_id`, `reacted_author_pubkey`, `zen_amount`, `comment`, `g1pub_dest`, `is_crowdfunding`, `project_id`, `bien_g1pub` | Paiement ZEN/G1 (kind 7) relayé depuis visiteur — **seulement si zen\_amount > 0** |

*(Extensible : `did_update`, `zen_payment`, `alert`, …)*

### Canal `bro_ia` — Relay des commandes BRO pour utilisateurs en roaming

Quand un MULTIPASS en roaming envoie un message kind 1 `#BRO` sur le relay de la station visiteur, `UPlanet_IA_Responder.sh` (station B) détecte le marker `.roaming`, lit `home.station` pour obtenir le NODE\_HEX de la home station, puis envoie un DM NIP-44 canal `bro_ia` plutôt que de traiter la commande localement.

```
Station B (visiteur) — UPlanet_IA_Responder.sh :
  1. Reçoit kind 1 #BRO de fred@example.com
  2. Détecte ~/.zen/game/nostr/fred@example.com/.roaming
  3. Lit home.station → HOME_NODE_HEX (64 hex)
  4. Construit payload bro_ia : {pubkey, event_id, lat, lon, message, url, kname}
  5. nostr_node_intercom.py send --channel bro_ia → DM NIP-44 → relay
  6. exit 0 (pas de traitement local)

Station A (home) — bro_dm_daemon.sh :
  1. inotifywait reçoit le fichier JSON dans bro_dm_queue/
  2. déchiffre via nostr_node_intercom.py decrypt
  3. channel == "bro_ia" → _handle_bro_ia()
  4. Appelle UPlanet_IA_Responder.sh avec les paramètres reconstruits
  5. Traitement complet avec les vraies clés joueur et le vrai uDRIVE
```

**Avantages** :

* La home station a les clés du joueur et accès à son uDRIVE/APP
* L'IPNS n'est publié que par la home station (source de vérité)
* Les ressources IA (Ollama, ComfyUI) de la home station sont utilisées
* La station visiteur reste légère (pas de traitement IA lourd)

### Résolution `home_node_hex` pour AMIS\_ROAMING

`HOME_IPFSNODEID` et `NOSTRNS` sauvegardés par `22242.sh` pour les utilisateurs `amisOfAmis_roaming` contiennent la **clé IPNS CIDv1** du joueur (`k51…`), pas le peer ID libp2p de la home station. La résolution suit donc cet ordre de priorité :

1. `user_dir/home.station` (cache local — mis à jour à chaque résolution réussie)
2. **strfry scan kind 0** du joueur → champ `home_station` (`IPFSNODEID:NODE_HEX_64`)
3. Scan `~/.zen/tmp/swarm/*/TW/{email}/` → `12345.json` → `NODEHEX` (SWARM\_ROAMING)
4. IPFS via `NOSTRNS/{email}/home.station` (lent, peut échouer)
5. IPFS via `/ipns/HOME_IPFSNODEID/{email}/home.station` (dernier recours)

**Prérequis** : la home station doit avoir publié le champ `home_station` dans le profil kind 0 du joueur via `nostr_setup_profile.py` (fait lors de la création MULTIPASS).

## Identités NODE

Chaque station a une paire de clés NOSTR dédiée :

* `~/.zen/game/secret.nostr` : `NSEC=…; NPUB=…; HEX=…`
* Créée lors de l'initialisation via `Ylevel.sh`
* Le HEX est publié dans le fichier `home.station` du MULTIPASS : `~/.zen/game/nostr/EMAIL/home.station` → `IPFSNODEID:NODE_HEX`

La station visiteur lit `home.station` depuis le répertoire local ou l'IPNS du joueur pour connaître le HEX de la home station et lui envoyer les DMs.

## Déclenchement

| Qui                                            | Quand             | Rôle                                            |
| ---------------------------------------------- | ----------------- | ----------------------------------------------- |
| `/api/fileupload` (Station B)                  | À chaque upload   | DM NIP-04 immédiat → home station               |
| `UPlanet_IA_Responder.sh` (Station B)          | Kind 1 #BRO reçu  | Détecte .roaming → DM bro\_ia → home station    |
| `bro_dm_daemon.sh` (Station A)                 | Queue DM inotify  | Canal bro\_ia → UPlanet\_IA\_Responder.sh local |
| `NOSTRCARD.refresh.sh` DM listener (Station A) | Chaque cycle      | Réception CIDs + dépôt APP/uDRIVE               |
| `NOSTRCARD.refresh.sh` CLEANUP (Station B)     | Chaque cycle      | Supprime comptes roaming > 24h inactifs         |
| `backfill_constellation.sh`                    | Toutes les heures | Propagation kind 4 inter-relays                 |

## Sécurité

* DMs chiffrés NIP-44 (ChaCha20-Poly1305 + HKDF-SHA256) : seule la home station peut déchiffrer
* Le CID IPFS garantit l'intégrité du fichier par contenu adressable
* La home station vérifie que l'email du payload correspond à un MULTIPASS local (non-roaming)
* Après envoi réussi, le fichier local est supprimé sur la station visiteur
* La clé NSEC utilisée est celle du NODE (pas du joueur) — séparation des responsabilités

## Voir aussi

* [media\_upload.py](https://github.com/papiche/Astroport.ONE/blob/master/UPassport/routers/media_upload.py) — `_maybe_send_roaming_dm()` + `/api/fileupload`
* `NOSTRCARD.refresh.sh` — DM listener (ligne \~325), PULL roaming (ligne \~1686)
* `NIP-101/relay.writePolicy.plugin/filter/22242.sh` — Création répertoire roaming éphémère
* `tools/make_NOSTRCARD.sh` — Écriture de `home.station` lors de la création MULTIPASS
* `IA/UPlanet_IA_Responder.sh` — Détection `.roaming` → DM channel `bro_ia` vers home station
* `IA/bro_dm_daemon.sh` — Canal `bro_ia` : `_handle_bro_ia()` appelle UPlanet\_IA\_Responder localement


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://astroport-1.gitbook.io/astroport.one/docs/reference/roaming_udrive_sync.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
