Skip to content

feat(appuplink): point the command uplink at FTW Cloud - #1633

Draft
frahlg wants to merge 1 commit into
masterfrom
cursor/command-uplink-cloud-fab2
Draft

frahlg wants to merge 1 commit into
masterfrom
cursor/command-uplink-cloud-fab2

Conversation

@frahlg

@frahlg frahlg commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Problem and result

Remote commands still leave the house through an outbound socket to the Frankfurt relay (wss://relay.ftw.energy). FTW Cloud already has a separate data uplink (wss://cloud.ftw.energy/v1/box, from the work in #1595). A parallel change is teaching ftwcloudd the relay's frame format so the same Noise sessions can be forwarded there.

This is the home-box half. app_link.command_endpoint chooses the command door:

  • omitted, or wss://relay.ftw.energy: today's behavior, no second dial
  • wss://cloud.ftw.energy: the box dials that origin and appends /r/{epoch}/{handle}/box and /exchange as it does for the relay. If the WebSocket handshake does not complete, that attempt falls back to the relay. The next attempt tries the cloud again.

Any other origin is refused at config load. A settings save that moves the door asks for a restart, because the uplink is dialed at startup.

Noise still ends at the phone. The box still only dials out. The cloud, like the relay, forwards opaque bytes and holds no key the box accepts for a change of state.

Scope and safety

The data uplink cannot carry these frames, so the sockets are not multiplexed:

  • cloudwire.Downlink is challenge, hello_ok, watch, resync and purged
  • a text frame or a non-cloudwire frame closes that socket
  • go/internal/cloudlink may import only go/protocol, so command handling cannot live there

The exchange socket and the dead-man HTTPS origin follow the door that accepted the box socket, so a phone and a watch meet in the same room and a farewell is posted to the door that is actually holding the connection.

Open PRs also touch go/cmd/ftw/main.go (#1628, #1364) and go/internal/config/config.go (#1051, #1003) for unrelated work. This change is additive in those files.

Default stays the relay. Nothing here publishes a release.

Verification

Passed locally:

  • go test ./internal/appuplink/ ./internal/config/ -count=1
  • go test ./cmd/ftw/ -count=1 -run 'CommandDoor|AppLink'

Covered: omitted endpoint stays the relay with no fallback; the cloud door falls back when the handshake is refused and is preferred when it accepts; the exchange socket stays on the door that accepted the box; a path (including /v1/box) is refused; changing the door requires a restart.

No UI change. Not run against a live ftwcloudd command door; that half is the parallel port.

Checklist

  • The change follows VISION.md and one selected scope.
  • I checked overlapping PRs and coordinated shared files/contracts.
  • Relevant checks cover the changed behaviour and failure paths.
  • A human reviewed changed UI in a browser, or no UI changed.
  • A Changeset is included, or the change is exempt.
  • Every commit has a DCO sign-off.
Open in Web Open in Cursor 

The box still dials the Frankfurt relay unless app_link.command_endpoint
names wss://cloud.ftw.energy. That door speaks the same Noise frames on
its own outbound socket. The data uplink cannot carry them: its downlink
is a closed set and it closes on a relay frame. A cloud handshake that
does not complete falls back to the relay, and the exchange socket and
dead-man rows follow the door that accepted the box.

Default behavior is unchanged.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
@frahlg

frahlg commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Please pause this. Fredrik decided on 15 October 2026 that commands move to FTW Cloud by a tunnel inside the existing /v1/box and /v1/app sessions, not by porting the relay's /r/{epoch}/{handle} protocol onto the cloud host. A second box socket with a relay fallback would lock in what ADR 0010 retires, and a box on this door would be unreachable from every shipped iPhone build. Details and the order of work: the latest review comment on #1632.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants