Commit Graph
8 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 b87afa8992 refactor(vpn): drop the 'This device' heading
The card sits directly under the connection toggle and shows this device's own name,
address and key expiry. The only other card that could be confused with it is the peer
list, which is plainly a list. A heading that repeats what the content already says
costs a line of vertical space on a phone and tells nobody anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 16:12:29 +01:00
pastilhasandClaude Opus 5 409ee11e9e fix(vpn): show the sign-in form when there is nothing to sign into
A state existed that the screen could not render: the core reaches NeedsLogin, no
profile is persisted, nothing connects.

signingIn is then true, so the manual form was suppressed as 'already in progress'. But
there was no profile, nothing was connecting, and nothing would ever complete. What the
screen showed was 'Waiting for sign-in' above a toggle whose value is bound to
connected — so it could not move, and tapping it appeared to do nothing — with no form,
no chooser, and (until the previous commit) no reset. The only exit was deleting the app
from the home screen.

A node in NeedsLogin with no profile is not signing in. It is stuck, and the way out of
stuck is the form that asks where to connect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 01:03:55 +01:00
pastilhasandClaude Opus 5 7672cf57bb fix(vpn): the reset must never be conditional on the state being sane
'Forget all connections' was shown only when registered || connected ||
profiles.length > 0 — evidence that something had worked.

A half-finished sign-in leaves none of those true. The core reaches NeedsLogin, no
profile is persisted, nothing connects — and the only control that could recover the app
was hidden, because it was reading the very state that had broken. The screen said
'Waiting for sign-in' with no way to choose another server, enter another key, or start
over. Deleting the app from the home screen was the only exit, on a real phone, at one
in the morning.

The comment above that gate already described the same mistake in a different case,
which is the tell: every condition here is a bet that the app knows its own state, and
this button exists for the times it does not. So there is no condition now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:59:45 +01:00
pastilhasandClaude Opus 5 e3024a7818 fix(settings): read the build number from expo-application
The row said '1.0.0 (dev)' on a build stamped 2026081502.

Constants.nativeBuildVersion is deprecated in SDK 57 and returns nothing — not an
error, not a throw, just undefined, so the fallback fired and the screen reported the
opposite of the truth with complete confidence. A deprecation that quietly returns
undefined is the worst kind: everything looks like it is working.

expo-application is the documented replacement and reads the value from the bundle at
runtime, which is the number OAS stamps before signing.

Also removes the last use of expo-constants here, and with it the '1.0.0' half — the
marketing string that has never changed and cannot tell two builds apart.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:21:12 +01:00
pastilhasandClaude Opus 5 e7e2d8c6b7 feat(settings): show our versioning, not Apple's marketing string
The row read '1.0.0 (2026081501)'. The first half has said 1.0.0 for every build ever
made here and conveys nothing — nobody maintains it, and it cannot distinguish two
builds. What identifies a build is the date-stamped number OAS assigns and writes into
CFBundleVersion before signing.

So the row now shows exactly that, dotted for reading: 2026.08.15.01. The same string
the install page shows, which is the point — 'is the thing on my phone the thing on the
site?' becomes a question you answer by looking rather than by remembering which link
you tapped.

A build that never went through OAS has no such number. Anything that is not exactly
ten digits is shown verbatim as 'dev (…)' rather than sliced into a fake date.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 00:12:32 +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 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