Skip to content

feat(contracts): PKNT wire protocol + RAM .pkst assembler - #168

Merged
doodlewind merged 1 commit into
mainfrom
feat/pknt-wire
Aug 3, 2026
Merged

feat(contracts): PKNT wire protocol + RAM .pkst assembler#168
doodlewind merged 1 commit into
mainfrom
feat/pknt-wire

Conversation

@doodlewind

Copy link
Copy Markdown
Collaborator

Part 1/2 of the Vita networking series (stacked on #167; series goal: the svc mailbox + video plane over WiFi TCP for hosts without a shared filesystem).

What

Contracts (contracts/spec/spec.ts, no op-table changes, spec.rs regenerated):

  • WIRE_*: framed TCP protocol 'PKNT' — 8-byte header (type·flags·rsv·len ≤ 256 KiB), forward-compatible skip-by-length; handshake (device sends app id, host acks version); UDP discovery beacon 'PKDB' on 8621, TCP listener on 8622.
  • Message table: ping/pong, ctrl (one mailbox JSON line), file (side files pushed before the ctrl line referencing them → synchronous loadImgFile hits a warm cache), streamOpen (verbatim 96-byte .pkst header block = full ring reset), videoSlot (flag bit0 = PackBits-RLE indices), audioChunk, streamMark (epoch/ended).
  • One TCP connection by design — ordering is load-bearing, exactly what the file transport got for free.

Core (engine/core, both no_std):

  • wire.rs: bounds-checked parsers + fixed-size encoders, stream.rs hardening discipline.
  • stream_rx.rs: RamStream — a byte-exact .pkst file image in RAM. payload-first/latestSeq-after preserved verbatim; stream.rs readers (parse_header_block/slot_offset/…) run unchanged over buf().

Proof

6 new cargo tests, the load-bearing one: the committed TS-written youtube-golden.pkst reconstructs byte-identically when its resident slots/chunks are re-fed as WIRE messages through RamStream — socket transport ≡ file transport, cross-language. Plus: truncation fuzz on every parser, RAM-vs-independent-reference-writer equality, RLE decode, no cursor published on rejected payloads. bun tests/contract.ts green.

Next in series: hosts/vita svc/vid/audio over this wire (net.rs + threads).

🤖 Generated with Claude Code

doodlewind added a commit that referenced this pull request Jul 23, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@doodlewind
doodlewind force-pushed the feat/touch-goldens branch from 2f0e92b to c4b94c1 Compare July 23, 2026 12:50
doodlewind added a commit that referenced this pull request Jul 23, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@doodlewind
doodlewind force-pushed the feat/touch-goldens branch from c4b94c1 to 247d666 Compare July 23, 2026 12:55
doodlewind added a commit that referenced this pull request Jul 23, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@doodlewind
doodlewind force-pushed the feat/touch-goldens branch from 247d666 to ab36340 Compare July 23, 2026 13:04
doodlewind added a commit that referenced this pull request Jul 23, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
doodlewind added a commit that referenced this pull request Aug 3, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from feat/touch-goldens to main August 3, 2026 05:49
The svc mailbox (+side files +.pkst streams) only exists over PSPLINK's
usbhostfs file share, which the Vita does not have. This defines the same
semantics over one socket, contracts-first, hosts later:

- spec.ts WIRE section: 'PKNT' framed TCP protocol (8-byte header: type,
  flags, u16 rsv, u32 len ≤ 256 KiB; unknown types skip by length), UDP
  'PKDB' discovery beacon (ports 8621/8622), handshake, and message table —
  ping/pong, ctrl (one mailbox JSON line), file (proactive side-file push so
  synchronous loadImgFile hits a warm cache), streamOpen (verbatim 96-byte
  .pkst header block = full ring reset), videoSlot (bit0 = PackBits-RLE
  indices), audioChunk, streamMark (epoch/ended). ONE connection on purpose:
  TCP ordering is load-bearing (streamOpen before the announce line, file
  before the results line). No op-table changes; spec.rs regenerated.

- engine/core/src/wire.rs: no_std bounds-checked parsers + fixed encoders
  (the stream.rs discipline — hostile lengths return None, never panic).

- engine/core/src/stream_rx.rs: RamStream — a byte-exact .pkst FILE IMAGE
  in memory. Slots/chunks apply payload-first, latestSeq-after, preserving
  the torn-frame contract verbatim; the exact same stream.rs readers then
  run over buf(), so file-backed (PSP) and socket-backed (Vita) transports
  share one reader and one golden format.

Tests (cargo test, 6 new): header/hello/beacon/payload round-trips with
per-byte truncation fuzz; RAM image == an independent reference file writer,
byte for byte (laps, marks); RLE slots decode via codec::packbits_decode and
bad geometry never publishes a cursor; and the committed TS-written
youtube-golden.pkst reconstructs byte-identically when re-fed as WIRE
messages — the cross-language proof that the socket transport IS the file
transport.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@doodlewind
doodlewind marked this pull request as ready for review August 3, 2026 05:50
@doodlewind
doodlewind merged commit 9d44b37 into main Aug 3, 2026
2 checks passed
@doodlewind
doodlewind deleted the feat/pknt-wire branch August 3, 2026 05:50
doodlewind added a commit that referenced this pull request Aug 3, 2026
The Vita host registered only ops 1–29: no svc channel, no video plane, no
audio, no network stack. This implements the whole family (ops 30–38) over
the PKNT wire protocol:

- net.rs: sceNet init confined to one function (module load, 1 MiB static
  pool, sceNetCtlInit; failure is remembered and final — Vita3K has no
  network stack, so svcOpen stays false and emulator e2e never regresses);
  everything after rides std::net over the newlib shims. Supervisor thread
  (host.txt override → UDP beacon discovery → connect + handshake → rx
  loop: CTRL → line queue, FILE → 6 MiB LRU cache, SLOT/CHUNK/MARK →
  RamStream, PING → PONG) + tx thread draining an mpsc channel. On
  disconnect the ring is marked ended, 1 s backoff, rediscover. The 60 fps
  main thread never blocks: its ops pop queues and take short-held mutexes.

- svc.rs (ops 30–33): PSP guest semantics verbatim — non-blocking svcOpen
  the app's connect pump retries, batched svcPoll, side files resolved from
  the proactive-push cache so the synchronous loadImgFile never waits on
  the network.

- vid.rs (ops 34–37): the PSP vid.rs reader over RamStream::buf() — same
  epoch/lap/seq-validation logic, same stage-then-present split, minus the
  IO budget (reads are memcpys). present() commits in the GPU-idle window:
  Runtime::render now runs begin_frame → vid::present → render_over, and
  graphics grows update_texture_in_place (rewrites the existing vita2d
  texture's pixels — the register/recycle path would allocate + drain GXM
  once per presented frame at 24 fps). The GE race discipline, GXM edition.

- audio.rs: the PSP design with sceAudioOut — BGM port at 44.1 kHz, SPSC
  ring (~743 ms), starvation sleeps, port opened AND released on the main
  thread (the channel-leak class of bug). Integer ×1/×2/×4 upsampler keeps
  PSP-profile 22.05 kHz streams playable.

- stats.rs + debugStats (op 38): PSP shape + a net section (rxBytes/
  txBytes/reconnects/slotsRx/fileEvicts); build.rs bakes the FNV-1a64
  bundle hash for the stale-embed tripwire.

Validated: full release VPK build (im-main) with the vita toolchain;
Vita3K e2e cursor-main 5/5 byte-exact (new modules dormant without svcOpen).
The wire/ring logic itself is covered by the engine/core tests (#168);
transport verification is real-hardware only (Vita3K has no sceNet).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
doodlewind added a commit that referenced this pull request Aug 3, 2026
* feat(vita): svc mailbox + video plane + audio over WiFi TCP

The Vita host registered only ops 1–29: no svc channel, no video plane, no
audio, no network stack. This implements the whole family (ops 30–38) over
the PKNT wire protocol:

- net.rs: sceNet init confined to one function (module load, 1 MiB static
  pool, sceNetCtlInit; failure is remembered and final — Vita3K has no
  network stack, so svcOpen stays false and emulator e2e never regresses);
  everything after rides std::net over the newlib shims. Supervisor thread
  (host.txt override → UDP beacon discovery → connect + handshake → rx
  loop: CTRL → line queue, FILE → 6 MiB LRU cache, SLOT/CHUNK/MARK →
  RamStream, PING → PONG) + tx thread draining an mpsc channel. On
  disconnect the ring is marked ended, 1 s backoff, rediscover. The 60 fps
  main thread never blocks: its ops pop queues and take short-held mutexes.

- svc.rs (ops 30–33): PSP guest semantics verbatim — non-blocking svcOpen
  the app's connect pump retries, batched svcPoll, side files resolved from
  the proactive-push cache so the synchronous loadImgFile never waits on
  the network.

- vid.rs (ops 34–37): the PSP vid.rs reader over RamStream::buf() — same
  epoch/lap/seq-validation logic, same stage-then-present split, minus the
  IO budget (reads are memcpys). present() commits in the GPU-idle window:
  Runtime::render now runs begin_frame → vid::present → render_over, and
  graphics grows update_texture_in_place (rewrites the existing vita2d
  texture's pixels — the register/recycle path would allocate + drain GXM
  once per presented frame at 24 fps). The GE race discipline, GXM edition.

- audio.rs: the PSP design with sceAudioOut — BGM port at 44.1 kHz, SPSC
  ring (~743 ms), starvation sleeps, port opened AND released on the main
  thread (the channel-leak class of bug). Integer ×1/×2/×4 upsampler keeps
  PSP-profile 22.05 kHz streams playable.

- stats.rs + debugStats (op 38): PSP shape + a net section (rxBytes/
  txBytes/reconnects/slotsRx/fileEvicts); build.rs bakes the FNV-1a64
  bundle hash for the stale-embed tripwire.

Validated: full release VPK build (im-main) with the vita toolchain;
Vita3K e2e cursor-main 5/5 byte-exact (new modules dormant without svcOpen).
The wire/ring logic itself is covered by the engine/core tests (#168);
transport verification is real-hardware only (Vita3K has no sceNet).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(vita): a stale host.txt falls back to beacon discovery

First hardware bring-up caught it immediately: the Mac's DHCP lease moved,
the ux0:data/pocketjs/host.txt override pointed at a dead address, and
because the override took precedence unconditionally the transport retried
it forever — beacon discovery could never rescue the session. Connect
failures now alternate the next discovery round onto the beacon listener,
so a stale override degrades to broadcast discovery instead of wedging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
doodlewind added a commit that referenced this pull request Aug 3, 2026
…cklist

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
doodlewind added a commit that referenced this pull request Aug 3, 2026
…cklist (#170)

The companion document for the PKNT wire (#168) and the Vita svc/video/
audio host (#169): why one ordered TCP connection, why the RAM ring is a
byte-exact .pkst file image, the thread/ownership diagram, the GPU-idle
present discipline, and the real-hardware verification checklist (Vita3K
has no network stack, so the transport only proves itself on a device).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant