Skip to content

About

DuoDuo Pocket: native iPhone app for DuoDuo and the Passport push-to-talk device

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

多多随身 (DuoDuo Pocket)

iPhone client for one ambient room, and the phone side of the Passport push-to-talk accessory.

DuoDuo Pocket puts your DuoDuo in your pocket: hold a button, say what you need, let go, and the answer comes back to the phone and the Passport screen. Product page: openduo.ai/docs/pocket.

DuoDuo Pocket on iPhone: a conversation with a voice note from the Passport, hold to talk, ambient mode speaking an answer, and photos and files

The Passport screen: hold OK to talk, listening, thinking with the transcript, and DuoDuo's answer

The app with its built-in demo content, and the Passport running the pocket firmware.

  • A native SwiftUI conversation with 多多 for one room: text, photos and files, hold-to-talk, and an ambient mode in which the phone listens to the room and speaks the answers (docs/design/native-app.md). History comes from the channel and is cached for offline reading.
  • The app reaches the channel over an embedded Tailscale node (tsnet, userspace); no VPN profile.
  • The Passport (a FoloToy AI Passport running the openduo/pocket-passport firmware) streams Opus over BLE while its button is held; the app uploads one voice note per press, tells the Passport the transcript, and forwards the answer to its screen.
  • Hold-to-talk in the app records with the phone microphone and uses the same voice-note path.

Get the app

DuoDuo Pocket is in TestFlight beta. Apply with the sign-up form; the TestFlight invitation arrives by email once the application is approved. Keep automatic updates on for DuoDuo Pocket in TestFlight: when your ambient at home is upgraded, an older app stops talking to it and shows 多多随身需要更新 until it is updated. Without a DuoDuo of your own, tap 先体验 (Try it first) on the first screen to see the app with built-in demo content.

What it needs

  • duoduo, the DuoDuo daemon, on your host.
  • The ambient channel, @openduo/channel-ambient from openduo/ambient, beside the daemon with a pocket room, reachable on your tailnet. The duoduo-ambient skill walks your duoduo through it: npx -y skills add https://github.com/openduo/duoduo --skill duoduo-ambient.
  • Optionally, a FoloToy AI Passport running the pocket firmware from openduo/pocket-passport.

Architecture

SwiftUI views ◀── AppModel (main actor; snapshots only while active)
                    ▲            ▲              ▲
ConversationStore ──┘  AmbientController ──┘   PocketEngine ──┘
 cache · cursor ·        the /live socket ·     Passport link · voice notes ·
 outbox · turn state     VoiceIO (AVAudioEngine, AEC, Opus)    RESULT · replies
        │                        │                          │
        └──────────── tsnet (Go, gomobile) ─────────────────┴──▶ channel host (tailnet, HTTPS)
                       /api/state · /api/imlog · /api/messages · /api/upload · /api/attachment
                       /live (events; hello + Opus while ambient is on)
Passport ──BLE──▶ PassportLink ─▶ PocketEngine ─▶ RESULT / REPLY / REPLY_DONE / APP_STATE / WORK
  • Everything dials out through tsnet; nothing listens on the phone.
  • One /live socket per room: conversation events always; with ambient on, the app says hello on it to hold the room's capture seat. Turning ambient off closes it and opens a new one without hello. Each connection resumes from the last row the app holds (since=<seq>).
  • Typed text and voice notes are messages (POST /api/messages, one Idempotency-Key each): a 202 accepts them, and their text, delivery and answer arrive as /live events.
  • Background budget: per audio frame only O(append) work on audio queues; SwiftUI is updated only while the app is active, from coalesced snapshots.
  • Background modes: bluetooth-central (state restoration relaunches the app for Passport traffic), audio (ambient mode keeps listening when locked, started and ended by the user) and fetch (Background App Refresh catches up answers for the Passport).

Requirements

  • Xcode with the iOS 17 SDK or later, XcodeGen, CMake.
  • Go and gomobile (go install golang.org/x/mobile/cmd/gomobile@latest && gomobile init).
  • A channel host from ambient reachable on your tailnet.

Setup

  1. scripts/build-opus.sh and scripts/build-tsbridge.sh.
  2. Config/Signing.local.xcconfig (gitignored) with DEVELOPMENT_TEAM = <team>. If your team cannot use the bundle id dev.openduo.pocket, set POCKET_BUNDLE_ID = <your id> there too.
  3. DEVICE_UDID=<udid> scripts/build.sh. CONFIGURATION=Release builds without the Debug fixtures and probes.
  4. First launch walks through Tailscale login, channel host + room (with a live check) and the Passport. Settings keep host, HTTPS/port and room in UserDefaults.
  5. Debug builds show design fixtures with -PocketDemo <screen> (list in docs/design/native-app.md §11), e.g. xcrun devicectl device process launch --device <udid> dev.openduo.pocket -- -PocketDemo chat-working -ui.appearance dark.

Testing without a Passport

  • Tools/PassportSim/run.sh runs the device side of the link on a Mac (pairing, INFO, presses of synthesized speech in real time, KEEPALIVE until REPLY_DONE). Commands go to logs/control.
  • Tools/mock-channel answers /api/messages, /live, /api/imlog and /api/state with fake transcripts, for testing the upload/reply path without a channel.
  • Use a phone that does not share an Apple ID with the Mac: Continuity holds its own LE link to the Mac and can block connects.

Docs

  • docs/design/native-app.md — product UI design and decisions.
  • docs/ble-protocol.md — the Passport link and voice-note contract, as implemented.
  • docs/constants.md — every constant with its basis.
  • docs/opus.md — libopus build choice.
  • docs/design/icon/final/ — the app icon source.

Security

See SECURITY.md. The app talks only to a channel on your own tailnet.

License

The code, including the 多多 avatar artwork, is FSL-1.1-Apache-2.0 (see LICENSE). The app icon, the 多多 / DuoDuo and OpenDuo names and the design mockups are OpenDuo brand assets with no open licence; forks must replace them (see NOTICE). Bundled third-party code (libopus, Tailscale's tsnet and its Go dependencies) keeps its own licences; see THIRD_PARTY_NOTICES.md.

About

DuoDuo Pocket: native iPhone app for DuoDuo and the Passport push-to-talk device

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages