Commit Graph
7 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 de433794ac fix: pin the native Expo packages that arrive transitively
The app installed and then died at launch, before the splash screen, with dyld:

  Symbol not found: ExpoModulesCore.BaseModule.willDestroy()
  Referenced from: ExpoFileSystem.framework
  Expected in:     ExpoModulesCore.framework

expo-file-system was 57.0.4 here against 57.0.1 in the monorepo, with the same
expo-modules-core 57.0.7 underneath. The newer file-system calls a method that patch of
modules-core does not have.

The gap was in how this repo was pinned. Direct dependencies were fixed to versions the
monorepo proves; transitive ones were not, and expo-file-system arrives through expo, so
it resolved fresh to whatever was newest. Comparing both trees found six native packages
that had drifted this way — asset, dev-launcher, dev-menu, file-system,
modules-autolinking, server — now pinned through overrides to the monorepo's versions.

Same family as the expo-image-picker trap already written down in CLAUDE.md: a wrong
Expo version typechecks, installs, and fails much later in a way that looks like a
broken toolchain rather than a wrong dependency. Here it was worse than a build error —
it built, signed, installed, and crashed on a phone.

The crash log named it in one line. That file remains the fastest diagnostic available.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:07:36 +01:00
pastilhasandClaude Opus 5 fe389c9752 feat(settings): show which build this is
Two OffScale builds from different source trees were installed on the same phone and
were indistinguishable: both reported 1.0.0 (1), because nothing ever stamped a version.
The only way to answer 'did that actually install?' was to remember which URL it came
from, which is not available to the person holding the phone.

Reads nativeBuildVersion rather than the value in app.json. The config says what the
project asked for; the native plist says what shipped, and it is the second one that
answers the question. OAS stamps its YYYYMMDDNN into CFBundleVersion before signing, so
the row now shows the same number the install page does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:24:02 +01:00
pastilhasandClaude Opus 5 859a716b5d fix(build): empty array expansion breaks under set -u on bash 3.2
scripts/build-android.sh died with 'MINIFY_ARGS[@]: unbound variable' before Gradle
ever ran. macOS ships bash 3.2, where expanding an EMPTY array under set -u is itself
an unbound-variable error — the array is declared and empty, which is the normal case,
since MINIFY defaults to off.

The monorepo's copy of this code has the same bare expansion and works, because that
script does not set -u. So the bug was latent there and became real the moment a
stricter copy ran it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:17:11 +01:00
pastilhasandClaude Opus 5 5846e80ddf chore(gocore): the golang.org/x versions this core was actually built with
gomobile upgraded six indirect golang.org/x modules while building the core — crypto,
mobile, mod, net, text, tools — and go.mod recorded it. Committing so a fresh clone
compiles the same core rather than whatever those pseudo-versions resolve to next week.

All indirect, all golang.org/x, no change to the tailscale.com pin at v1.102.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:04:23 +01:00
pastilhasandClaude Opus 5 79fbfee5fb fix: build standalone — five things the monorepo was quietly providing
The extraction typechecked from day one and had never been built. It took five
attempts, each failing further along, and every cause was the same shape: something the
shared node_modules or the monorepo's layout supplied without anyone declaring it.

1. The config plugin had the monorepo's geometry written into it —
   ../../../packages/officer-net/ios/libs, correct from apps/<name>/ios and meaningless
   from a repo root. It reported a path belonging to neither repo. Now derived from the
   plugin's own location with path.relative, so it is right wherever the module sits.

2. That fix appeared to do nothing, because officer-net was a  dependency and bun
   COPIES those. Prebuild ran a week-old copy while the source was edited — a clean edit
   and a stale build, the same shadow-copy trap that cost a night in the monorepo.
   Making it a workspace turns node_modules/officer-net into a symlink.

3. @babel/plugin-transform-react-jsx was never declared by anything. It was hoisted into
   the monorepo by some other package, so babel-preset-expo with jsxImportSource found
   it by luck.

4. Same for react-native-reanimated and react-native-worklets: nativewind depends on
   react-native-css-interop, which lists reanimated as a PEER, whose babel plugin
   requires react-native-worklets/plugin. Three levels of nobody-declared-it.

5. And the self-inflicted one. Adding  for (2) silently flipped bun to its
   isolated linker — only direct dependencies at top level, everything else symlinked
   into node_modules/.bun, which Metro cannot resolve. It surfaced as 'Unable to resolve
   module react-native-css-interop/jsx-runtime', naming a package that was installed and
   in the lockfile and simply not where the resolver looks. bunfig.toml now pins both
   properties that are needed at once: workspaces for the symlink, hoisted for the
   layout.

Result: OffScale.app, 82 MB, with OffTailTunnel.appex at 20 MB carrying 129 Tailcore
symbols — the Go core is in the extension, which is the half that matters.

Not yet run on a device. It builds; that is a different claim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:04:09 +01:00
pastilhasandClaude Opus 5 c63662082a docs: record where the Tailscale logo permission actually stands
Tailscale replied on 2026-08-14, after a week, that their logo is not to be used for
MARKETING purposes.

That is not this use. The mark appears in the attribution strip inside an installed
app, naming the project the client speaks to — no store listing, no screenshot, no
website, and this repository is not published anywhere it reads as promotion. So it
stays, deliberately and provisionally, on that reading.

Written into the component and the NOTICE rather than left in a conversation, because
the decision has two halves and only one of them is obvious later. The first is that
it may stay for now. The second is that it must NOT migrate into a store listing or a
promotional image on the strength of already being in the app — that is exactly the
use they declined, and it is the mistake this note exists to prevent.

If they say remove it, it is one component and its two call sites. That containment is
why it was worth keeping rather than pre-emptively cutting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 21:15:11 +01:00
pastilhas ef3582f2aa init: OffScale — standalone MIT Tailscale/Headscale client
Extracted from the officer-suite monorepo into its own public repo. The app is
open source and free; the rest of the suite is not, so it could not stay in a
tree that ships an account layer.

What moved:

- apps/offscale/*                  -> repo root (App shell, app.json, assets)
- packages/officer-net/            -> modules/officer-net/, a `file:` dependency
- packages/core VPN closure        -> src/vpn/ (screen, hook, native bridge, invite)
- packages/core shared primitives  -> src/ui/ (theme, cards, tabs, backdrop, splash)
- scripts/build-{ios,android}.sh   -> scripts/, cut down to this one app

What deliberately did NOT move: the platform account layer. VpnScreen used to
render core's AppSettingsScreen, which drags in ServersSection -> servers.ts,
useAuth, api.ts, shared-session, install-guard, api-keys. None of that exists
here. src/settings/SettingsScreen.tsx is a fresh replacement rendering exactly
what VpnScreen passed it — the app's own sections plus About — with no Servers
tab, no Appearance control (the app is pinned to dark) and no sign-out.

The one platform reference left in the closure was a dead `import { request }
from './api'` in officer-net.ts, unused since enrollVpn() was removed. Gone. The
app now contacts nothing but the control server you point it at, its peers, and
the enrolment sidecar named inside an invite link you tapped.

Dependencies are pinned to the exact versions the monorepo has installed, not to
ranges: this repo starts from versions already proven to build, and an unpinned
install is a supply-chain decision nobody made. react-query, react-dom,
react-native-screens, expo-secure-store, track-player, expo-audio and
expo-file-system were all declared by the old app and imported by none of this
code, so they are not here.

NOTICE carries Tailscale's BSD-3 text, names the built artifacts that contain
their compiled code, and treats the trademark question separately — including
that src/ui/TailscaleMark.tsx reproduces their logo in the attribution strip.
2026-08-14 20:32:17 +01:00