OverviewFilesCommitsIssuesDocsReleasesPeers

favela-web commits / 2c19fd7

show the version of the code that is RUNNING, not the version on disk
`pgh app status` reports the INSTALLED tree's package.json. That is true about the disk and can be false about the process: an app that installed an update and never restarted still reports the new number while serving the old one — a trap this fleet has hit before. And favela-web registered with the sidecar as `version: null`, so nothing anywhere reported what was actually running. Now: the process states its own version, and it appears in Settings — signed out too, since checking what you are looking at should not require an account. On the phone this is the ONLY place a version appears at all: a handset has no `pgh app status`, and its worklet bundle arrives by OTA independently of any APK, so "is this the build I expect" was previously unanswerable there. lib/version.js imports package.json statically so bare-pack pulls it into the module graph — the phone runs from worklet.bundle with no package.json on disk, so anything read via fs would be absent exactly where it is needed most. The `with { type: 'json' }` attribute is load-bearing: node (the test suite's runtime) throws ERR_IMPORT_ATTRIBUTE_MISSING without it, bare accepts it either way. Verified on both before choosing the form. Committed with some urgency: favela-android's CI vendors this package from the WORKING TREE, a build was in flight, and its own ci.yaml warns that whatever is checked out here ships to the fleet. Leaving modified tracked files during a vendored build is the race that comment exists to prevent. Suite green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2c19fd7fd87767450821781ec918ebbac00c75f2
by plankatron <fleeky@gmail.com> · 2026-08-25T00:02:53.000Z

Changed files (4) +36 -3

bin/favela-web.js +5 -1
……@@ -19,6 +19,7 @@ import { openCorestore } from 'atproto-store/lib/corestore.js'
1919import { OverlayRepoStore } from 'atproto-store/lib/overlay-repo-store.js'
2020import { roomKey16, blobPath, sniffMime, isSafeCid } from '../lib/blob-serve.js'
2121import { verifyBlobCid } from '../lib/blob-refs.js'
22import { VERSION } from '../lib/version.js'
2223import { parseHostedDids, createIsForeign, isIgnoredCollection, IGNORED_COLLECTIONS } from '../lib/hosted-dids.js'
2324import { join } from 'bare-path'
2425import { discoveryTopic } from 'atproto-identity/lib/discovery.js'
……@@ -3133,7 +3134,10 @@ try {
31333134 return { ok: true, willRespawn: true }
31343135 })
31353136 await svc.listen()
3136 maintainSidecarRegistration({ udsPath, name: process.env.PGH_APP_NAME || 'favela-web', runtime: 'bare', version: null })
3137 // Report the RUNNING version, not null. This was null, so `pgh app status` could only ever show the
3138// version on DISK — which is exactly the number that lies when an app installs an update and does not
3139// restart. Now the process states its own.
3140maintainSidecarRegistration({ udsPath, name: process.env.PGH_APP_NAME || 'favela-web', runtime: 'bare', version: VERSION })
31373141 console.log(`[favela] control surface on ${udsPath} — OTA respawn armed`)
31383142} catch (e) {
31393143 // Non-fatal by design: serving the timeline matters more than being remotely manageable.
lib/render.js +15 -1
……@@ -7,6 +7,7 @@
77// Pure + injected: takes plain view-models ({ posts, cursor }, a viewer descriptor) and returns
88// strings. It touches no P2P/index state, so it is unit-testable in isolation.
99import { SCOPE_COLLECTION } from './scopes.js'
10import { VERSION } from './version.js'
1011import { reachFor } from './reach.js'
1112import { isSafeCid } from './blob-serve.js'
1213
……@@ -908,6 +909,8 @@ header nav a[aria-current=page]{color:var(--fg);font-weight:600;border-bottom-co
908909.themebtn:hover{background:var(--bg-soft);color:var(--fg)}
909910.themebtn.on{background:var(--accent);color:var(--on-accent);font-weight:600}
910911.themenote{font-size:var(--f0);color:var(--fg-faint);margin:var(--s2) 0 0;max-width:44ch}
912.vernote{font-size:var(--f0);color:var(--fg-faint);margin:var(--s5) 0 0}
913.vernote code{font-size:var(--f0)}
911914/* ── composer ──────────────────────────────────────────────────────────────────────────────────────
912915 The composer was ~40% of the first screen on a phone, permanently expanded, with four gate selects
913916 competing with the timeline before a reader had seen a single post. The gates move behind a native
……@@ -1183,10 +1186,20 @@ export function themeControl (theme = null, returnTo = '/settings') {
11831186<p class="themenote">System follows your device. On Android the in-app browser does not report that setting, so choose Light or Dark there.</p></section>`
11841187}
11851188
1189// The version of the RUNNING code. `pgh app status` reports the version on DISK, which an app that
1190// installed an update and never restarted will report while still serving the old one — so a number
1191// rendered by the process itself is the only one that answers "what am I actually looking at".
1192//
1193// Shown SIGNED OUT as well, and on the phone this is the only place a version appears at all: the
1194// handset has no `pgh app status`, and its worklet bundle arrives by OTA independently of any APK.
1195export function versionNote () {
1196 return `<p class="vernote">favela-web <code>${esc(VERSION)}</code></p>`
1197}
1198
11861199export function settingsPage ({ viewer = null, profile = null, did = null, devices = [], theme = null } = {}) {
11871200 if (!viewer) {
11881201 // Appearance still renders here — it is not an account setting.
1189 return page({ title: 'settings · favela', viewer, active: null, theme, body: '<div class="viewhdr">Settings <a href="/">← back</a></div>' + themeControl(theme) + '<p class="empty">Sign in to manage your profile. <a href="/login">sign in →</a></p>' })
1202 return page({ title: 'settings · favela', viewer, active: null, theme, body: '<div class="viewhdr">Settings <a href="/">← back</a></div>' + themeControl(theme) + versionNote() + '<p class="empty">Sign in to manage your profile. <a href="/login">sign in →</a></p>' })
11901203 }
11911204 const cid = profile && profile.avatar && blobCid(profile.avatar.ref)
11921205 const current = (cid && did && isSafeCid(cid))
……@@ -1210,6 +1223,7 @@ export function settingsPage ({ viewer = null, profile = null, did = null, devic
12101223 theme,
12111224 body: `<div class="viewhdr">Settings <a href="/">← back</a></div>
12121225${themeControl(theme)}
1226${versionNote()}
12131227<section class="authbox">
12141228 <h3>Profile picture</h3>
12151229 <p class="empty">Signed in as <strong>${esc(viewer.did)}</strong>${profile && profile.displayName ? ' · ' + esc(profile.displayName) : ''}</p>
lib/version.js +15 -0
……@@ -0,0 +1,15 @@
1// The version of the CODE THAT IS RUNNING — not the version on disk.
2//
3// `pgh app status` reports the INSTALLED tree's package.json, which is true about the disk and can be
4// false about the process: an app that installed an update and never restarted still reports the new
5// number while serving the old one. That gap has bitten this fleet before. A version rendered by the
6// running process is the only one that answers "what am I actually looking at".
7//
8// Imported statically from package.json so bare-pack pulls it into the module graph — the phone runs
9// from worklet.bundle with no package.json on disk, and anything read via fs would be absent there.
10// The `with { type: 'json' }` attribute is required by node (the test suite's runtime) and accepted by
11// bare (the daemon's). The bare form alone throws ERR_IMPORT_ATTRIBUTE_MISSING under node; verified on
12// both before choosing this.
13import pkg from '../package.json' with { type: 'json' }
14
15export const VERSION = (pkg && pkg.version) || 'unknown'
package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-web",
3 "version": "0.3.77",
3 "version": "0.3.78",
44 "description": "favela-web \u2014 the web/SSR surface of Favela, the native P2P-local, community-owned social client + appview over the self-custody atproto stack (hyper-proto-peer). One of the Favela surfaces (alongside favela-cli and favela-native). bsky is an optional POSSE export, not the home. See DESIGN.md.",
55 "type": "module",
66 "main": "bin/favela-web.js",