Skip to content
C.W.K.
Lesson 05 of 05 · published

Running and Shipping

~14 min · launchd, port-registry, testflight, testing, operations

Level 0Sketch
0 XP0/43 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

One command, ports from the ledger

The engine starts with python -m backend.serve in the cwk-avatars Conda environment. Uvicorn binds every interface on the configured port with proxy_headers=False, and the comment above the call says what makes that safe: only loopback and tailnet peers pass the application's network gate. Binding wide and gating in the app is the pattern Solo Auth Quest calls the 0.0.0.0 decision, and turning proxy headers off means no forwarded header can make a request look like it came from somewhere else. The Protection Follows the Pixels track walks through the gates themselves.

No port is written in settings.py. The engine port and its Tailscale Serve HTTPS twin come from the family registry, a vendored copy of Firelink's port ledger, which places Avatars on engine 4100 and twin 4101, with Vite's dev server on 4102 listed as planned. Origins come from the same registry: Pippa and Firelink run on the studio's Mac, so they are dialed on loopback. Ember is dialed through its HTTPS twin, never a raw tailnet IP, and at this snapshot the registry places Ember on that same Mac too: the twin is the address the code reads, not a sign of distance. The public origin is the studio's own twin. The data root comes from the registry too, unless AVATARS_DATA overrides it for a test. Firelink Quest's track "One Owner Per Fact" is the doctrine underneath: a port has one owner, and everyone else reads it.

Literals that outlived the move

Two copies of those numbers survived. The iOS app's Engine.swift keeps static let twinPort = 4101, used only when the registry has no twin for Avatars and the app falls back to the Pippa host on that port. The dev proxy in vite.config.ts sends /api to http://127.0.0.1:4100. The house sign also says Vite must use strictPort, but neither the dev script (vite --host 127.0.0.1 --port 4102) nor the config sets it, so a busy 4102 would quietly move the dev server to another port. Today every value agrees with the ledger. They are still copies, and the day the ledger moves a port they become the bug. When code and docs disagree with the ledger, the ledger wins.

A LaunchAgent that asks where it belongs

scripts/install-service.py writes the LaunchAgent com.cwk.avatars-serve: run at load, keep alive, a ten-second throttle between restarts, logs under the data root. Its first act is a refusal. Avatars has one writer, and the script asks the registry with runs_here("cwkAvatars") whether this Mac is it. Until October 19 the check was a fleet label; when the family moved its services to a new host, asking the registry meant the installer moved with it. The last line it prints is a rule of evidence: inspect /api/health before claiming the service is live. Health answers the product name, ok and the version from cwk-product.json, which is 0.1.4.

The engine also serves the built web client from frontend/dist, with the shell sent as no-cache so an installed page revalidates on every launch. Rebuilding the client and restarting the service are separate steps, and a git pull does neither.

What the tests count

SuiteCount at f6cd664Status
Backend, pytest41 in 7 filesrun: 41 passed
Frontend, Vitest32 in 8 filesrun: 32 passed
iOS unit, XCTest40 in 5 filescounted; last recorded run 0 failures
iOS UI, XCUITest2counted; the throttled upload bench skips without its engine

The README and old commit messages carry smaller numbers from earlier days. A test count is a dated observation, so this table names its snapshot: cwkAvatars f6cd664, committed October 23, 2026. The iOS suites were counted there, and their last recorded run is fc0a21d on October 19. One backend test runs the family kit's own drift check against this checkout, so a hand-edited vendored file fails the suite; Sibling Kit Quest's track "A Check With Teeth" explains why that check lives in the consumer's tests.

TestFlight: four facts, kept apart

The iOS app ships through TestFlight, never a cable install. scripts/mobile-testflight.sh is vendored from the kit, one program for every family iOS app, and Avatars keeps only its facts beside it: bundle id com.cwk.avatars.mobile, the dedicated simulator, the inputs that must be committed, the build number from project.yml and the marketing version from cwk-product.json. Its guards run in one order, tests and a visual check of every first screen included, before the build number moves. Sibling Kit Quest's lesson "One Program, Many Configs" tells how eight hand-grown copies became that one program.

The pipeline's success line asserts one fact only. Delivery is four:

  1. Uploaded: App Store Connect has the build.
  2. Processed: Apple finished processing it.
  3. Assigned: the internal testing group has it.
  4. Installed: a tester's device has it. Apple's install count trails, so zero means not counted yet, never not installed.

There are pipeline commits for builds 1 through 21, and each one records an upload that passed the gate (Destination: upload), which is fact 1 of 4 and not a delivery. The README's own record keeps the facts apart the same way: build 8 was uploaded, processed and in internal testing, with installation on a device not yet counted. The product is v0.1.4; build 21 and the web package's 0.1.0 are different numbering domains.

Principle: Say which fact is true. "Shipped" is four facts, and only the last one is delivery.

Code

cwkAvatars, backend/settings.py: ports and origins read from the family registry, never spelled here (verbatim, lines 9-22)·python
# Engine port and its Tailscale Serve twin, from the family registry (cwkFirelink
# port ledger), never spelled here (cwkPippa todo #549).
ENGINE_PORT = int(FAMILY_SERVICES["cwkAvatars"]["engine"])
TWIN_PORT = int(FAMILY_SERVICES["cwkAvatars"]["https"])

# Family addresses from the vendored registry (cwkSiblingKit todo #442). The studio
# runs on service host, so Pippa and Firelink are dialed on loopback; Ember lives on the
# server Mac and is dialed through its HTTPS twin, never a raw tailnet IP. The
# public origin is the studio's own twin. Every host follows the machine's host
# profile (Pippa's Ark).
PIPPA_ORIGIN = loopback_origin("cwkPippa")
EMBER_ORIGIN = twin_origin("cwkEmber")
FAMILY_ORIGIN = loopback_origin("cwkFirelink")
PUBLIC_ORIGIN = twin_origin("cwkAvatars")
cwkAvatars, scripts/install-service.py: the LaunchAgent refuses a Mac the registry does not place Avatars on (verbatim, lines 17-30)·python
def main():
    # One writer, on the Mac the family registry places cwkAvatars on (service host
    # unless the host profile moves it, cwkPippa todo #549).
    if not runs_here("cwkAvatars"):
        raise SystemExit("Avatars has one writer, on the Mac the family registry places it on.")
    logs = engine_data_root("avatars") / "logs"
    logs.mkdir(parents=True, exist_ok=True)
    destination = HOME_DIR / "Library/LaunchAgents" / f"{LABEL}.plist"
    body = {"Label": LABEL, "ProgramArguments": [str(PYTHON), "-m", "backend.serve"], "WorkingDirectory": str(ROOT), "RunAtLoad": True, "KeepAlive": True, "ThrottleInterval": 10, "StandardOutPath": str(logs / "serve-stdout.log"), "StandardErrorPath": str(logs / "serve-stderr.log"), "EnvironmentVariables": {"PYTHONUNBUFFERED": "1", "PATH": f"{PYTHON.parent}:/usr/bin:/bin:/usr/sbin:/sbin"}}
    destination.write_bytes(plistlib.dumps(body))
    domain = f"gui/{os.getuid()}"
    subprocess.run(["launchctl", "bootout", f"{domain}/{LABEL}"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    subprocess.run(["launchctl", "bootstrap", domain, str(destination)], check=True)
    print(f"Installed {LABEL}; inspect /api/health before claiming it is live.")
cwkAvatars, scripts/testflight-facts.sh: the four delivery facts read back from App Store Connect (verbatim, lines 4-19)·shell
# The four delivery facts after a TestFlight upload, read from App Store Connect —
# cwkPippa/docs/IOS-NATIVE-DELIVERY.md §1 keeps them apart, and the pipeline's
# `*_UPLOAD_OK` line asserts only the first:
#
#   1 uploaded       App Store Connect has the build
#   2 processed      Apple finished processing it (processingState VALID)
#   3 assigned       it is in internal testing (internalBuildState IN_BETA_TESTING)
#   4 installed      a tester installed it (the build's install count)
#
#   testflight-facts.sh <bundle-id> <build-number> [--wait <seconds>]
#
# One line per fact, then a machine line:
#   TESTFLIGHT_FACTS build=<n> uploaded=yes|no processed=yes|no|failed assigned=yes|no installed=yes|not_counted|unknown
# Fact 4 is App Store Connect's usage metric, which trails installs (measured 2026-09-25: a
# year-long data point, counts not live). A count above zero is a fact; zero is "not counted
# yet", never "not installed" — the phone's TestFlight app stays the witness.

External links

Exercise

Take a small service of your own that hard-codes its port. Move the port into one JSON file that every component reads, the server, the dev proxy and any client, and delete every other literal. Then write an installer for a macOS LaunchAgent with RunAtLoad, KeepAlive and a ThrottleInterval that refuses to install unless the JSON file names this machine as the service's host, and that prints a reminder to check the health route. Finally, write a one-line status report for your last release that names which of the four delivery facts you can actually show.
Hint
grep for the port number across the repository before and after; the after count should be the JSON file alone. For the host check, compare the JSON's host field with the output of scutil --get LocalHostName, and remember that bootout before bootstrap makes the installer safe to run twice.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.