Files
dchain/client-app/app.json
vsecoder 1d9206494a feat(chain): multi-device registry (v2.2.0-alpha1)
PR #1 of the multi-device roadmap. Adds per-device X25519 keys registered
on-chain so senders can fan out envelopes across all of a recipient's
physical devices — fixes the single-device limitation where a second
phone / desktop loses messages as soon as the first one reads them.

Chain (blockchain/):
  - New event types LINK_DEVICE / UNLINK_DEVICE, signed by the identity's
    master Ed25519.
  - LinkDevicePayload {x25519_pub_key, device_name} +
    UnlinkDevicePayload {x25519_pub_key} on the wire.
  - State: prefixDevice + x25519_pub → DeviceRecord{owner, name,
    added_at, revoked_at?}; reverse index prefixDevicesByOwner for
    O(k) listing. Revoke is a soft-delete — the row stays as a visible
    tombstone so offline clients can detect their own revocation and
    wipe local state.
  - MaxDevicesPerOwner = 10 slot cap; MaxDeviceNameLen = 64.
  - Strict lowercase-hex validation on x25519_pub so clients can't
    desync on letter case.
  - Same-owner re-link is a rename/refresh (recreates reverse index too
    — needed after a revoke).
  - Chain.DevicesOf(master_pub) returns the active records; empty slice
    for legacy identities so senders can fall back to IdentityInfo.X25519Pub.

HTTP (node/):
  - GET /api/devices/{master_pub_or_addr} — returns {master_pub, count,
    devices[]}. Revoked records filtered out.
  - /api/identity/{pub} gains `device_count` so senders can decide
    upfront whether to fan out or take the legacy path.

Tests (blockchain/devices_test.go):
  - Happy paths (1, 3 devices), foreign-owner rejection, same-owner
    refresh after revoke, unlink removes from active set,
    foreign-signer unlink rejection, idempotent double-unlink,
    malformed pub/name rejection, MaxDevices cap + recovery after
    unlink frees a slot, empty list for unknown master.

Also in this commit:
  - deploy/single/join.sh — convenience script operators have been
    iterating on in this session (joiner-node bring-up + firewall
    port patching + Caddy opt-out).
  - client-app/app.json — `usesCleartextTraffic: true` on Android so
    installed APKs can talk to http:// dev nodes without TLS.

See docs/ROADMAP.md for PRs #2..#4 (client fan-out, pairing flow,
desktop Electron shell).
2026-04-22 16:20:07 +03:00

72 lines
2.0 KiB
JSON

{
"expo": {
"name": "DChain Messenger",
"slug": "dchain-messenger",
"version": "1.0.0",
"orientation": "portrait",
"userInterfaceStyle": "dark",
"backgroundColor": "#000000",
"ios": {
"supportsTablet": false,
"bundleIdentifier": "com.dchain.messenger",
"infoPlist": {
"NSMicrophoneUsageDescription": "Allow DChain to record voice messages and video.",
"NSCameraUsageDescription": "Allow DChain to record video messages and scan QR codes.",
"NSPhotoLibraryUsageDescription": "Allow DChain to attach photos and videos from your library.",
"ITSAppUsesNonExemptEncryption": false
}
},
"android": {
"package": "com.dchain.messenger",
"softwareKeyboardLayoutMode": "pan",
"usesCleartextTraffic": true,
"permissions": [
"android.permission.RECORD_AUDIO",
"android.permission.CAMERA",
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.MODIFY_AUDIO_SETTINGS"
]
},
"web": {
"bundler": "metro",
"output": "static"
},
"plugins": [
"expo-router",
"expo-secure-store",
[
"expo-camera",
{
"cameraPermission": "Allow DChain to record video messages and scan QR codes.",
"microphonePermission": "Allow DChain to record audio with video."
}
],
[
"expo-image-picker",
{
"photosPermission": "Allow DChain to attach photos and videos.",
"cameraPermission": "Allow DChain to take photos."
}
],
[
"expo-audio",
{
"microphonePermission": "Allow DChain to record voice messages."
}
],
"expo-video"
],
"experiments": {
"typedRoutes": false
},
"scheme": "dchain",
"extra": {
"router": {},
"eas": {
"projectId": "28d7743e-6745-460f-8ce5-c971c5c297b6"
}
}
}
}