Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/firmware.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ jobs:

- name: Run Python tooling selftests
run: |
python -m py_compile scripts/check_tdeck_budget.py scripts/fetch_tdeck_artifact.py scripts/serial_harness.py scripts/tdeck_smoke.py scripts/tdm_airtime_smoke.py
python -m py_compile scripts/build_app_package.py scripts/check_tdeck_budget.py scripts/fetch_tdeck_artifact.py scripts/serial_harness.py scripts/tdeck_smoke.py scripts/tdm_airtime_smoke.py
python scripts/tdm_airtime_smoke.py --selftest
python scripts/build_app_package.py examples/local-apps/weather-mesh --out /tmp/weather.mesh.zip --device-path /sd/limitlezz/packages/weather.mesh.zip
- name: Validate local app samples
run: |
python -m py_compile scripts/validate_local_app_samples.py
Expand Down
37 changes: 11 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,18 @@ iPhone-style dark look (status bar, battery glyph, grouped settings cards).
terminates on exit. Storage-enabled actions can increment a safe counter in
the app's scoped `data/` directory, unsupported action effects fail closed,
and apps with matching permissions can use read-only `{time}` / `{battery}`
tokens in foreground text. Loaded entry source plus app-controlled foreground
metadata are charged against a 704-byte resident runtime budget. SDK
apps with matching permissions can use read-only `{time}` / `{battery}`
tokens in foreground text, and apps with `notifications` can request a
feedback-service notification through a bounded `notify:` action effect. SDK
tokens in foreground text. Apps with `notifications` can request a
feedback-service notification through a bounded `notify:` action effect.
Loaded entry source plus app-controlled foreground metadata are charged
against a 704-byte resident runtime budget. SDK
`api_version` and permission metadata are parsed fail-closed, with rejected
package diagnostics visible in Developer Mode. Apps that request `storage`
get a scoped package `data/` directory prepared with a 64 KB launch-time quota
guard, and the App Store detail screen can clear only that app's scoped data.
Script execution, richer API injection, downloads, and updates are still TODO.
tokens in foreground text. SDK `api_version` and permission metadata are
parsed fail-closed, with rejected package diagnostics visible in Developer
Mode. Apps that request `storage` get a scoped package `data/` directory
prepared with a 64 KB launch-time quota guard, and the App Store detail screen
can clear only that app's scoped data. The future network catalog now has a
bounded `index.json` schema validator and serial `app catalog status|test`
diagnostics. Script execution, richer API injection, catalog fetch, downloads,
and updates are still TODO.
The network catalog path validates cached `limitlezz.app.catalog.v1` rows and
can install/update verified stored-ZIP packages from catalog metadata. Script
execution, richer API injection, signatures, and polished async progress are
still TODO.
- **App flash (`appfs`)** - T-Deck builds mount the FAT `appfs` partition at
`/appfs` without formatting, expose it beside SD/local storage in Files, and
scan `/appfs/apps` even when the SD card is absent.
Expand Down Expand Up @@ -399,23 +393,14 @@ for local apps and read-only inspection when present.
local apps into the SDK 0.1 foreground shell with bounded app-provided actions
and scoped storage counters plus read-only `{time}` / `{battery}` tokens;
Close/Esc terminates the foreground session instead of leaving it resident;
unsupported action effects launch-block instead of being ignored; the static
catalog remains a prototype (GET -> "..." -> OPEN).
unsupported action effects launch-block instead of being ignored; cached
catalog rows now show real GET/UPDATE/OPEN state and route installs/updates
through the verified package transaction.
- **Local app sample pack** - `examples/local-apps/` contains copyable SDK 0.1
packages for Calculator, Field Notes, Offline Maps, Weather Mesh, Mesh BBS,
Signal Scope, LoRa Chess, and APRS Bridge; CI validates that each package
stays inside the firmware's bounded manifest, permission, token, action, and
scoped-storage rules.
and scoped storage counters plus read-only `{time}` / `{battery}` tokens. The
foreground shell reports and enforces the 704-byte resident runtime metadata
budget; unsupported action effects launch-block instead of being ignored; the
static catalog remains a prototype (GET -> "..." -> OPEN).
unsupported action effects launch-block instead of being ignored; network
catalog schema validation exists, while fetch/download/install remains ahead;
the static catalog remains a prototype (GET -> "..." -> OPEN).
unsupported action effects launch-block instead of being ignored; the network
catalog has a CI-validated `index.json` schema, while fetch/download/install
are still prototype/future work (GET -> "..." -> OPEN).
- **Contacts / detail** — unified directory with network dots; detail page
with Message (jumps into the bound conversation) and spec table.
- **Settings** — airtime scheduler bar that rebalances live when the
Expand Down
77 changes: 29 additions & 48 deletions docs/tdeck-app-catalog-schema.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,46 @@
# T-Deck App Catalog Schema

This is the first Network App Store increment. It defines and validates the
future `index.json` shape, but it does not fetch, download, install, update, or
uninstall packages yet.
This page is kept as a compatibility pointer for older roadmap links.

Catalog indexes are expected at:
The canonical network App Store contract is now:

- `/sd/limitlezz/catalog/index.json`
- `/appfs/catalog/index.json`
- `docs/tdeck-network-app-catalog.md`
- `docs/examples/app-catalog-index.json`
- `scripts/validate_app_catalog.py`

The index must fit in `4096` bytes and use this top-level shape:
Package archives:

```json
{
"schema": "limitlezz.app_catalog.v1",
"updated": "2026-06-18T00:00:00Z",
"apps": []
}
```

Each app entry is bounded and fail-closed:

```json
{
"id": "weather.mesh",
"name": "Weather Mesh",
"version": "0.1.0",
"author": "Limitless",
"description": "Local weather reports",
"icon": "weather",
"hue": 48,
"api_version": "0.1",
"compatibility": "tdeck",
"permissions": ["display", "network_wifi"],
"download_url": "https://apps.example.invalid/weather.mesh.zip",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"size": 32768,
"screenshots": ["https://apps.example.invalid/weather.bmp"]
}
```

Validation rules:

- `id` uses the same safe token rules as local app manifests.
- `api_version` must be supported by the local SDK compatibility gate.
- `permissions` must use the existing allowlist.
- `download_url` and optional `screenshots` must be `http://` or `https://`
URLs without whitespace or control characters.
- `sha256` must be exactly 64 hex characters.
- `size` must be nonzero and no more than `2 MB` for this first package path.
- `hue`, if present, must be `-1` or `0..359`.
- The catalog can list up to `24` apps.
- The first firmware-native archive path is a `.zip` using ZIP method `0`
only, meaning stored/uncompressed entries.
- Whole-package `sha256` and exact byte size must match before extraction.
- The package must include root `manifest.json`; the embedded manifest `id`
must match the requested install id.
- File names must be relative, must not contain `..`, backslashes, colons,
absolute roots, hidden path segments, or a top-level `data/` tree.
- Each file is capped at `256 KB`, each package at `2 MB`, and each package at
`24` files.
- Extraction happens into a hidden staging directory, then promotion validates
the manifest and rolls back on failure.

Serial diagnostics:
Firmware validates and loads the canonical `limitlezz.app.catalog.v1` schema.
The lower-level package transaction can install a verified package file already
present on SD/appfs.

```text
app catalog status
app catalog test
app catalog install-test
app catalog install <id> [package_path]
app package test
app package install <id> <path> <sha256> <bytes>
```

`app catalog status` validates a cached index if one exists and otherwise
reports that no cached catalog is present. `app catalog test` runs a built-in
valid/invalid schema selftest so hardware smoke can prove the parser without
requiring Wi-Fi or SD setup.
requiring Wi-Fi or SD setup. `app catalog install-test` proves catalog metadata
drives install/update and that a bad catalog hash preserves the prior app.
`app package test` creates and installs a small stored-ZIP package on-device and
proves hash mismatch, id mismatch, unsafe path, unsupported compression,
rollback, and update behavior.
20 changes: 19 additions & 1 deletion docs/tdeck-app-developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Not implemented yet:
- arbitrary Lua/script execution
- background tasks
- network catalog download/update
- app package signatures or SHA verification
- app package signatures
- mesh send/receive APIs
- notifications API behavior
- raw hardware, radio, filesystem, or kernel access
Expand Down Expand Up @@ -61,6 +61,23 @@ data/ optional; prepared automatically for storage apps
assets/ optional; reserved for later richer runtimes
```

Installable archive packages use a stored-only `.zip` subset for the first
firmware installer:

```sh
python scripts/build_app_package.py examples/local-apps/weather-mesh \
--out weather.mesh.zip --device-path /sd/limitlezz/packages/weather.mesh.zip
```

The script prints the exact package byte count and SHA256 digest for:

```text
app package install <id> <path> <sha256> <bytes>
```

Archives must include root `manifest.json`, use relative file paths only, avoid
hidden path segments and top-level `data/`, and store entries uncompressed.

## Minimal App

Create a package directory:
Expand Down Expand Up @@ -259,6 +276,7 @@ The native selftest creates sample local app packages and checks:
- over-quota blocking
- token permission gating
- appfs-only discovery
- stored-ZIP package install/update rollback

For release or PR validation, use the project workflow:

Expand Down
Loading