19 · Pairing a device
alix’s web server can lend a paired phone its AI backend for the tutor, the
exam, deck generation, and
note-taking, over /api/remote/*: the phone keeps its own decks and
progress, the desktop only computes answers.
The pairing token changes on every restart
alix --lan prints a fresh, random pairing token each time the server
starts. This is the single biggest papercut in pairing a device: if an app
that paired fine yesterday suddenly can’t reach the server, the token most
likely changed on the last restart. Re-pair with the freshly printed URL, or
pin one that never changes:
[serve]
token = "pick-your-own-fixed-token"
With token set, --lan reuses it instead of generating a new one, so a
saved pairing survives restarts. A pinned token (or a --token value) must
be at least 16 characters; --lan refuses a shorter one before it binds.
What the remote surface does
Nothing under /api/remote/* writes the server’s own progress store,
session, decks, or recent list; it only computes an answer and hands it
back. A tutor question re-sends the whole conversation with every call,
since the server keeps no session for a remote client. When the stable deck
and card ids match what the desktop serves, the tutor uses the desktop’s
assembled card, notes, links, frozen evidence, and allowed live source root,
just like the desktop tutor. When either id cannot be matched, the reply is
marked card_only and uses only the card text the phone sent; the phone names
that limit as “Answered from the card alone: the paired desktop could not
match this card.” An AI exam sitting is
graded on the server, but the result, any remediation cards, and what counts
as mastered stay the phone’s to keep. A deck generation call hands back the
full deck text and a suggested file name; a note condense hands back up to
three lines. Either way the phone decides where they land: the decks folder
or the deck file, never the server.
The server side of this ships from 0.6.0; see docs/API.md, section 4.11,
for the wire contract if you’re building against it.
Pairing the mobile app
On your computer, run alix --lan and note the URL it prints (the same one
[serve] token can pin, above). On the mobile app:
- Open Settings (the ☰ button on the deck list) and tap Connected devices.
- Paste the printed URL into the sheet and tap Pair.
The app checks the server before saving anything, so a bad paste or an unreachable desktop never gets stored silently. It shows one inline line naming what went wrong:
- an unparseable paste:
that does not look like an alix pairing URL - a desktop it can’t reach:
no alix answered at <host>:<port> - a desktop too old for this app’s remote surface:
alix <version> found, this app needs 0.6.0 or newer - a desktop that answers but rejects the token (most often a server that
restarted, and minted a fresh token, since the URL was printed):
alix answered but refused this token. Copy a fresh pairing URL from the server.
On success the sheet closes with a note of which host you paired with. The
same Connected devices row reopens the sheet later, now showing the
current host:port and an Unpair button; unpairing only clears the
saved config, nothing else on the phone changes.
What’s borrowed once paired
Once paired, review gains things it doesn’t have offline:
- An Ask chip, shown once you’ve attempted the current card (revealed
it, picked a choice, submitted a typed answer, or walked all its lines)
but not before: the same attempt-first rule the web tutor follows. It opens
the same question/answer flow as the desktop tutor: replies render with the
card’s own content grammar (code blocks, checklists, tables, quotations),
and Make this a card and Make this a note enable once an answer
exists that nothing was made from yet. A note condenses the exchanges
since the last note into key points and appends them to the deck’s
personal sidecar,
<deck>.local.md, on the phone and, when the desktop serves that deck, on the desktop too, so the next sync pull carries the same note back rather than a sidecar without it (an empty result says so rather than doing nothing silently). A card you add from a draft is minted on the desktop the same way when it serves the deck, and the phone records it under the id the desktop hands back, so the next pull keeps it. Pressing Add locks the draft’s fields. A refusal (a duplicate, or a malformed card) adds nothing on the phone, says so, and unlocks the draft for editing. When no answer arrives, the phone says the card may already be on the desktop and keeps the fields locked: tapping Add again sends the same card, and if the desktop already kept it, it hands that card back and the phone records it. For a deck the desktop does not serve, the card is minted on the phone alone and stays there. Every question re-sends the whole exchange to the paired desktop (the server keeps no session of its own for a remote turn). The conversation belongs to the card, not the sheet: closing the sheet while the desktop is still working loses nothing, the answer or note lands anyway, a note saved with the sheet closed is announced on the review screen, and reopening the sheet shows the whole transcript and a question you had typed but not yet sent. Moving to another card starts a fresh conversation; while the previous card’s note or card draft is still being made, the new card’s tutor says so and waits, since the desktop makes one thing at a time. A desktop restarted mid-call forgets the call; the phone reports it as failed (the question goes back into the chat box) rather than waiting for a result that will never come. - An Exam row on a deck’s launch sheet, for a fact deck that declares exam material, with an “Only what needs work” switch (on by default, disabled once every section has passed). It opens a full-screen exam: one question at a time, then the points count, each section’s count, a Pass/Partial/Fail breakdown per question and, when an answer missed key points, a Turn the gaps into cards button. Every sitting’s results and any remediation cards land in the phone’s own progress store, exactly like an offline grade, matching the rule above: the server computes, the phone keeps.
A trace deck runs entirely on-device: predict, reveal, and self-grade each checkpoint. It has no AI exam or paired-only capstone.
The Settings page also gains a Generate deck row: give it a URL and
optional guidance, the desktop generates the deck text the same way
alix deck generate does, then the phone asks where to save it (an in-app
folder browser) and writes it under a collision-free file name. It follows
the same liveness rule as the two chips: the row
appears only while the phone has confirmed the paired desktop is reachable
and new enough, and is simply absent otherwise.
The Ask chip and the Exam row both depend on the phone having confirmed the paired desktop is reachable and running at least version 0.6.0; there is no retry chrome for a dead or too-old server: the Ask chip simply is not there, and the Exam row is disabled with a short hint.
If the desktop answers but rejects the token partway through a review, an
exam, a note, or a generation (the restart case above, caught mid-session
instead of at pairing time), the phone shows one SnackBar: “Pairing expired.
Pair again from Settings → Connected devices.” On the review and exam screens it
carries a Re-pair action that reopens the pairing sheet directly; the
tutor sheet shows the same line inside the sheet instead, so there you
follow the message’s own instruction. Pinning [serve] token is what stops
this from happening in the first place.
Security posture
This is plain HTTP on your local network. The bearer token guards against someone stumbling onto the server by accident, not against a hostile network: anyone already on your LAN who gets hold of the token can use it. For anything beyond your own LAN, put alix behind a VPN or a reverse proxy; alix itself will not grow TLS or accounts.