Skip to content

fleet: serve the EC2 half's aggregator to browsers under agg-ec2 names - #1268

Merged
ejc3 merged 8 commits into
mainfrom
fix/issue-1232-ec2-aggregate
Oct 25, 2026
Merged

ejc3 merged 8 commits into
mainfrom
fix/issue-1232-ec2-aggregate

Conversation

@ejc3

@ejc3 ejc3 commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Third of three stacked PRs for #1232. The EC2 half's aggregator, which runs beside the coordinator in its Fargate task, gets the task's own addresses and agg-ec2-<token> names instead of the coordinator's Cloudflare tunnel (Access-gated, and Cloudflare in the media path).

Stacked on: fix/issue-1232-host-aggregate (#1267), which is stacked on #1266.

What changed

fcvm fleet coord --ec2-aggregate-tls (needs --ec2-aggregate-listen, --tls-secret, --dns-token-secret):

  • src/fleet/coord/ecs.rs reads the task's addresses at start: the global IPv6 address from the ECS task metadata (ECS_CONTAINER_METADATA_URI_V4), and the public IPv4 address of the task's network interface from EC2 DescribeNetworkInterfaces, filtered by its private address (the metadata does not carry it). A task with neither is a start error.
  • The coordinator writes the certificate from --tls-secret as tls.json into --ec2-aggregate-tls-dir (default /run/fcvm-fleet/tls; the agent's tls::refresh/run, hourly) and runs the aggregator with --tls-listen 0.0.0.0:443 and [<ipv6>]:443. The Fargate task is its own host, so both families use 443.
  • Config carries the two addresses; dns::facts adds agg-ec2 (generation 0, both on 443) through the same naming path; owned() recognises agg-ec2-<token>[-v4]; published names are kept in Fleet::ec2_aggregate_published and cleared when a coordinator resumes (they were for the previous task's addresses).
  • view: with the DNS writer the agg:ec2 entry's URLs are its published names, else null; without it, --ec2-aggregate-url as before.
  • --ec2-aggregate-listen now needs --ec2-aggregate-url or --ec2-aggregate-tls.
  • fleet/coord/Containerfile: creates /run/fcvm-fleet owned by 65534 for the task volume mounted there (root file system read-only).

The task also needs 443 in its security group, net.ipv4.ip_unprivileged_port_start=443 (the image runs as 65534) and the ECS dualStackIPv6 account setting for an IPv6 address: that is ejc3/aws#671, behind a switch that is off. Staging is unchanged until the coordinator is given --ec2-aggregate-tls.

Contract and evidence

  • the_ec2_aggregator_is_published_at_the_coordinators_addresses (tests/test_fleet_coord.rs): with the addresses in Config and one EC2 tile, the AAAA and A records for agg-ec2-<token> are written, no URL reaches the page before confirmation, then wss://agg-ec2-<token>.<zone>/frames and the -v4 name with no port. Without the agg-ec2 fact it fails: no record is ever written (label_for times out on an empty zone).
  • the_task_addresses_are_read_from_metadata_and_ec2: dual stack, IPv4 only, a ULA address ignored, an empty network list refused, the EC2 answer's publicIp.
make test-unit FILTER="-E 'binary(/test_fleet/) or package(fleet-core) or test(/fleet::/) or test(/routed/)'"
     Summary [   5.663s] 175 tests run: 175 passed
make lint  # ok

Not verified (needs the live Fargate task): that a Fargate task volume takes /run/fcvm-fleet's ownership from the image, and the ECS metadata field names on a dual-stack task (taken from the ECS task metadata v4 documentation). Either failing stops the coordinator at start with the error, only with --ec2-aggregate-tls passed.

Fixes #1232 (ejc3/aws#671 holds the switch that turns it on).

ejc3 added 3 commits October 25, 2026 12:10
The EC2 aggregator runs beside the coordinator in its Fargate task and
was reachable only at --ec2-aggregate-url, through the coordinator's
Cloudflare tunnel (Access-gated, and Cloudflare in the media path).

`fcvm fleet coord --ec2-aggregate-tls` (with --ec2-aggregate-listen,
--tls-secret and --dns-token-secret) gives it the task's own addresses:
- src/fleet/coord/ecs.rs reads them at start: the global IPv6 address
  from the ECS task metadata, and the public IPv4 address of the task's
  network interface from EC2 DescribeNetworkInterfaces (filtered by its
  private address; the metadata does not carry it). A task with neither
  is an error.
- The coordinator writes the certificate from --tls-secret as tls.json
  into --ec2-aggregate-tls-dir (the agent's tls::refresh/run, hourly)
  and runs the aggregator with --tls-listen on 0.0.0.0:443 and
  [<ipv6>]:443.
- Config carries the two addresses; dns::facts adds `agg-ec2` (generation
  0, both on 443), owned() recognises agg-ec2-<token>[-v4], and the
  published names are kept in Fleet::ec2_aggregate_published (cleared
  when a coordinator resumes, since they were for the previous task's
  addresses).
- view: with the DNS writer the agg:ec2 entry's frame_url/frame_url_v4
  are its published names, else null; without it, --ec2-aggregate-url
  as before.
- --ec2-aggregate-listen now needs --ec2-aggregate-url or
  --ec2-aggregate-tls.

The task must admit 443 in its security group and lower
net.ipv4.ip_unprivileged_port_start (the image runs as 65534); that is
the Terraform side. Staging is unchanged until it passes the flag.

Tests: the_ec2_aggregator_is_published_at_the_coordinators_addresses
(tests/test_fleet_coord.rs) waits for the AAAA and A records, sees no
URL before confirmation, then wss://agg-ec2-<token>.<zone>/frames and
the -v4 name with no port. Without the agg-ec2 fact it fails: no
record is ever written. the_task_addresses_are_read_from_metadata_and_ec2
covers dual stack, IPv4 only, a ULA address and the EC2 answer.
A host aggregator's stream was reachable only at the plain URL its agent
reported, which an https page cannot open, so AggregateView.frame_url
was unusable and frame_url_v4 was always null.

Agent (`--aggregate-tls-port <port>`, with --tls-secret):
- runs the aggregator with --tls-file (the renderers' tls.json) and
  --tls-listen on <publish ip>:<port>, and with an IPv6 prefix also on
  [<prefix>::1]:443. <prefix>::1 is the host's side of the prefix, which
  fcvm never gives a VM; the agent puts it on the host's default IPv6
  interface (ip -6 addr replace ... nodad) before it starts.
- reports ipv6, and with --public-ipv4-from-imds ipv4 and v4_port, in
  the aggregate message (new optional fields).

fleet-core:
- Host keeps the reported addresses and its published names
  (aggregate_published). Event::Published for `agg-<host>` applies only
  to the addresses the host reports now.
- aggregate_id(host): `agg-<host>` when the host name fits a DNS label.
- canonical_ip and Published::names replace the two copies of the
  canonical comparison (dns.rs, view::frame_urls).
- view: with published names an aggregate's frame_url/frame_url_v4 are
  its published names, else null; without DNS, the reported URL as
  before.

Coordinator:
- dns::facts includes each host aggregator (generation 0: one launch
  while its addresses stay the same), so its names go through the same
  mint/write/confirm/publish path and zone as slot names; owned()
  recognises agg-<host>-<token>[-v4]. The write/confirm loop is
  unchanged.
- The poster reads a host aggregator at its published name when it has
  one: the reported URL can be an address only that host reaches.

Staging is unchanged until an agent is given --aggregate-port and
--aggregate-tls-port.

Tests:
- a_host_aggregator_is_published_under_its_own_names
  (tests/test_fleet_coord.rs): no URL for a plain aggregator, records
  written, URLs only after confirmation, a new address hides the old
  URLs and the old records go. With the view returning the reported
  URL it fails: "the plain URL reached an https page".
- the_agent_runs_and_feeds_its_aggregator now runs the aggregator with
  TLS, checks the reported ipv4/v4_port, https /healthz and 404 for
  /layout over TLS.
- only_fleet_names_are_owned, labels_are_owned_and_not_reused,
  a_host_aggregator_is_named_only_when_its_host_name_fits_a_label,
  a_slot_address_is_the_gpu_index_plus_two_in_the_prefix.
…vm-fleet

The coordinator image runs as 65534 on a read-only root file system, so
--ec2-aggregate-tls needs a directory it can write: --ec2-aggregate-tls-dir
defaults to /run/fcvm-fleet/tls (the agent's own default is
/run/fcvm-fleet/tls too), and the Containerfile creates /run/fcvm-fleet
owned by 65534 for the task volume mounted there.
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 54 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0059ca9c-b600-4b3e-b24b-04436a40d3ea
Commits

Reviewing files that changed from the base of the PR and between 7b6ab7b and 98ab62d.

Files selected for processing: 15
  • fleet/PROTOCOL.md
  • fleet/coord/Containerfile
  • fleet/core/src/apply.rs
  • fleet/core/src/protocol.rs
  • fleet/core/src/state.rs
  • fleet/core/src/view.rs
  • src/cli/args.rs
  • src/fleet/agent/addr.rs
  • src/fleet/agent/mod.rs
  • src/fleet/coord/dns.rs
  • src/fleet/coord/ecs.rs
  • src/fleet/coord/mod.rs
  • src/network/routed.rs
  • tests/test_fleet_agent.rs
  • tests/test_fleet_coord.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 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-25T13:36:30.809619Z 14d0461 Manual request
ℹ️ 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.

@ejc3

ejc3 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@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: 14072a7b1d

ℹ️ 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 thread src/fleet/coord/ecs.rs
…ly one

Codex on #1268: DescribeNetworkInterfaces filtered by the task's private
IPv4 address can match an interface in another VPC of the account that
uses the same address, and parse_public_ip took the first <publicIp> in
the answer, so the EC2 aggregator could be named at another host's
public address.

The query now filters by mac-address, the interface's MAC from the task
metadata (MACAddress), falling back to the private address only when the
metadata has no MAC, and parse_public_ip gives an address only when the
answer names exactly one interface.

Test: an_answer_naming_two_interfaces_publishes_neither_address. Before
the fix: left: Some("192.0.2.44"), right: None. With the single-interface
check removed again it fails the same way.
@ejc3

ejc3 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 14d0461a25

ℹ️ 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".

@ejc3

ejc3 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

NOT-A-DEFECT: Codex's review body on the earlier head carries the finding answered in its thread (fixed, RED-VERIFIED, and Codex cleared 14d0461); the CodeRabbit comment is a review-limit notice.

@ejc3

ejc3 commented Oct 25, 2026

Copy link
Copy Markdown
Owner Author

NOT-A-DEFECT: the CodeRabbit comment is a review-limit notice. The head is a clean automatic merge of the layer below (its tree equals git merge-tree --write-tree of its parents), so Codex's no-findings review of this PR's previous head covers its own change.

@ejc3
ejc3 changed the base branch from fix/issue-1232-host-aggregate to main October 25, 2026 21:40
@ejc3
ejc3 merged commit 223fe90 into main Oct 25, 2026
11 of 16 checks passed
@ejc3
ejc3 deleted the fix/issue-1232-ec2-aggregate branch October 25, 2026 22:09
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.

fleet: serve the aggregate streams to browsers over IPv4 and IPv6 (TLS, per-launch names)

1 participant