Skip to content

feat(cloud): security notices for every owner's phone, naming the client only while the copy carries events - #1661

Merged
frahlg merged 6 commits into
masterfrom
claude/cloud-notices
Oct 16, 2026
Merged

frahlg merged 6 commits into
masterfrom
claude/cloud-notices

Conversation

@frahlg

@frahlg frahlg commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

What

Security notices reach every owner's phone through FTW Cloud, with the readable copy on or off. This is the box and protocol half; the service half is srcfl/ftw-cloud#23, and the phone half is srcfl/ftw-app#77.

#1646 already made the box send notice {kind} whenever the link is up. This PR checks that and closes three gaps:

  • The client's name, only when the grant allows it. notice may carry client on agent.added, and only while the readable copy is on and its grant has ftw.events.read, as an event's arguments would. With the copy off the notice holds the kind alone. The name must pass cloudwire.NoticeClientOK: at most 65 characters (the consent page keeps 64 and adds "…"), no control or format character. FTW Cloud knows the name from the roster's agent row whatever the copy; the copy decides only whether it also reaches Apple. ADR 0008 and the docs now say so.
  • One alert, owners only. The five notice kinds no longer go to FTW Cloud as event (QueueEvent refuses them). With the copy on, an owner got the event and the notice; a viewer with events got the event.
  • A notice cannot carry readings. cloudwire.ReadNotice decodes a notice strictly: any field beyond the header, kind and client refuses it, as do a boot that is not 16 bytes, a kind outside NoticeKinds, or a name on another kind or that NoticeClientOK refuses. FTW Cloud uses it before it sends anything to Apple.
  • A notice lasts a day. It states a lasting fact, so ReadAlert accepts one up to NoticeMaxAgeMs (24 h) old, and FTW Cloud gives Apple the same expiry. An event of a notice's kind reads as the generic line.
  • The box counts what it drops. A notice its full queue (16) pushes out is logged and counted in noticesDropped (GET /api/cloud).

ReadAlert, the reference for the phone's notification extension, now reads a signed notice in an alert as an event of its kind, with client as its one argument. The alert's aps stays the same for every kind.

Docs: docs/protocol-cloud.md (notice table, Push, byte budget, what Apple sees) and docs/agents.md (Push kinds).

Pairs

Catalogue

agent.added now reads "An assistant or Sourceful was given access to your box. See who has access in the app." (review S6), edited in srcfl/ftw-webapp#94 first and copied here with push_gen.go regenerated. The box renders it with no name of its own; the phone names the client from the notice.

Contract-pair: srcfl/ftw-webapp@8d80b8e

Deploy order

Cloud first (srcfl/ftw-cloud#23), then boxes (this), then phones (srcfl/ftw-app#77). A box that sends notices to a service that drops them loses nothing but those pushes; a service ready first drops the old event copies at once.

Tests

  • cloudwire: ten new push vectors (notice: agent added, with the client's name, owner added, a kind that is no notice, a name on another kind, a notice that carries a reading, a notice kind sent as an event, a notice twenty hours old, one over a day old, one with a short boot). The earlier cases keep their bytes.
  • cloudwire.TestReadNoticeTakesOnlyANotice.
  • cloudlink: TestANoticeGoesWithTheCopyOff now also checks that no name goes with the copy off; TestANoticeNamesTheClientOnlyWhenTheGrantCarriesEvents (with a cut name and a format character); TestANoticeKindIsNoEvent; TestAFullNoticeQueueCountsWhatItDrops; TestANoticeGoesPastTheBudget.
  • cmd/ftw: TestAnAgentAddedAtTheBoxPageNamesItsClient (add and widen).
  • GOFLAGS=-p=2 make verify: clean (vet, tests, build).

🤖 Generated with Claude Code

…'s name only while the copy carries events

The box's notice may name the client on agent.added while the readable
copy is on and its grant has events. cloudwire.ReadNotice refuses a body
with anything beyond the header, kind and client, and ReadAlert reads a
signed notice in an alert as an event of its kind. The notice kinds no
longer go to FTW Cloud as events, so an owner gets one alert and a viewer
none. Push vectors gain six notice cases for the app to replay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T08:00:14.391573Z ec5db08 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec5db08b31

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +61 to +62
if slices.Contains(cloudwire.NoticeKinds, e.Kind) {
return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid recording delivered cloud notices as failures

When FTW Cloud is the only effective notification transport—for example, no ntfy provider and no web-push subscriptions—agentPorts.push successfully queues the separate notice and then calls NotifyAgents; the registered CloudPublisher reaches this branch, returns ErrNothingToSend, and notifications.Service.deliver consequently increments Failed and writes a failed history entry because every publisher appeared absent. Thus successfully delivered security notices are reported as failures; exclude these kinds before dispatching to the cloud event publisher or account for the notice delivery as a successful transport.

AGENTS.md reference: AGENTS.md:L22-L23

Useful? React with 👍 / 👎.

frahlg and others added 2 commits October 16, 2026 10:09
…nsent page cuts it, and count what the box drops

Review of #1661:
- ReadNotice checks the header (a 16-byte boot) and takes a client name
  of at most 65 characters, the consent page's 64 and its "…", with no
  control or format character (NoticeClientOK), which the box applies too.
- A notice states a lasting fact: the phone shows it for a day
  (NoticeMaxAgeMs). ReadAlert reads a notice kind sent as an event as the
  generic line, since that path reached viewers.
- The box counts and logs a notice its full queue drops (noticesDropped).
- ADR 0008 and the docs say Apple may see an agent's client name, and that
  FTW Cloud sees it in the roster whatever the copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frahlg and others added 2 commits October 16, 2026 11:04
Copied from srcfl/ftw-webapp (claude/agent-added-neutral, 8d80b8ea) and
regenerated. The box renders it with no name of its own; the phone names
the client from the notice when it has one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@frahlg
frahlg merged commit 7fb84d6 into master Oct 16, 2026
17 checks passed
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.

1 participant