fix: read the core's real permission header format (blank-page bug) #2

Merged
domuk merged 1 commit from fix/core-permission-format into main 2026-08-16 22:57:02 +01:00
Owner

Fixes the reason the module rendered a blank page even though it installed, ran healthy and served its assets correctly.

What was wrong

The core sends the resolved permission map as a JSON object:

x-ion-permissions: {"dashboard.view":true,"module.ion-homelab":"control","locations.view":true,...}

The module split that string on commas, shredding it into fragments like {"dashboard.view":true and "locations.view":true. Every permission check then failed, so every tab was filtered out and every route bounced to a redirect — an empty render with no console error, which is exactly why it looked like nothing was wrong.

The real contract, read off the core's proxy rather than assumed

Header Format
x-ion-user a bare username string, not JSON
x-ion-permissions a JSON object; values true / false / 'view' / 'control'
x-ion-permission-scope '*' for an admin, otherwise 'user'
x-ion-module-access none / view / control for this module

Changes

  • identity.js parses the object form, and still accepts a JSON array or CSV so a future change on the core side degrades to a permission error rather than a blank page
  • false and 'none' are correctly not granted; 'view' / 'control' are
  • admin is detected from the scope marker, since x-ion-user carries no isAdmin flag
  • module access falls back to module.<name> in the map when the header is absent
  • a malformed header now fails closed with 403 instead of parsing to nonsense
  • whoami reports scope, wildcard, and which of the module's 26 declared keys the caller actually holds — so the next mismatch is one request to diagnose
  • the client trusts the server's wildcard rather than re-deriving it

Verification

  • 56/56 tests pass, including 11 new regression tests pinning the core's real formats
  • Image rebuilt as 1.0.1 and verified in a container: parsed 11 keys from the JSON object, 10 of them the module's declared keys, scope and wildcard both resolved
  • Smoke check still passes end to end

Also in this release

updates/manifest.json bumped to 1.0.1 with the new image.

Separate issue, not fixed here

The manifest's "icon": "server" renders as literal text in the core's sidebar and module list, giving "serverIon-HomeLab". That is core-side icon handling, so it belongs in an Ion-OS change rather than this one.

Fixes the reason the module rendered a **blank page** even though it installed, ran healthy and served its assets correctly. ## What was wrong The core sends the resolved permission map as a **JSON object**: ``` x-ion-permissions: {"dashboard.view":true,"module.ion-homelab":"control","locations.view":true,...} ``` The module split that string on commas, shredding it into fragments like `{"dashboard.view":true` and `"locations.view":true`. Every permission check then failed, so every tab was filtered out and every route bounced to a redirect — an empty render with **no console error**, which is exactly why it looked like nothing was wrong. ## The real contract, read off the core's proxy rather than assumed | Header | Format | |---|---| | `x-ion-user` | a bare **username string**, not JSON | | `x-ion-permissions` | a **JSON object**; values `true` / `false` / `'view'` / `'control'` | | `x-ion-permission-scope` | `'*'` for an admin, otherwise `'user'` | | `x-ion-module-access` | `none` / `view` / `control` for this module | ## Changes - `identity.js` parses the object form, and still accepts a JSON array or CSV so a future change on the core side degrades to a permission error rather than a blank page - `false` and `'none'` are correctly **not** granted; `'view'` / `'control'` are - admin is detected from the scope marker, since `x-ion-user` carries no `isAdmin` flag - module access falls back to `module.<name>` in the map when the header is absent - a malformed header now **fails closed with 403** instead of parsing to nonsense - `whoami` reports scope, wildcard, and which of the module's 26 declared keys the caller actually holds — so the next mismatch is one request to diagnose - the client trusts the server's wildcard rather than re-deriving it ## Verification - **56/56 tests pass**, including **11 new regression tests** pinning the core's real formats - Image rebuilt as **1.0.1** and verified in a container: parsed 11 keys from the JSON object, 10 of them the module's declared keys, scope and wildcard both resolved - Smoke check still passes end to end ## Also in this release `updates/manifest.json` bumped to `1.0.1` with the new image. ## Separate issue, not fixed here The manifest's `"icon": "server"` renders as **literal text** in the core's sidebar and module list, giving "serverIon-HomeLab". That is core-side icon handling, so it belongs in an Ion-OS change rather than this one.
The module installed, ran healthy and served correctly, but rendered a blank
page. Cause: the core sends the resolved permission map as a JSON OBJECT

  {"dashboard.view":true,"module.ion-homelab":"control",...}

and the module split that string on commas, shredding it into fragments like
'{"dashboard.view":true'. Every permission check then failed, so every tab was
filtered out and every route bounced — an empty render with no console error,
which is why it looked like nothing was wrong.

Read off the core's proxy rather than assumed, the actual contract is:
  x-ion-user              a bare username string, not JSON
  x-ion-permissions       a JSON object; true / false / 'view' / 'control'
  x-ion-permission-scope  '*' for an admin, else 'user'
  x-ion-module-access     none | view | control for this module

Changes
- identity.js parses the object form, and still accepts a JSON array or CSV so a
  future change on the core side degrades to a permission error, not a blank page
- false and 'none' are correctly NOT granted; 'view'/'control' are
- admin detected from the scope marker, since x-ion-user carries no isAdmin flag
- module access falls back to module.<name> in the map when the header is absent
- a malformed header now fails closed with 403 instead of parsing to nonsense
- whoami reports scope, wildcard and which declared keys the caller actually
  holds, so the next mismatch is one request to diagnose
- client trusts the server's wildcard rather than re-deriving it

11 regression tests pin the real formats. 56/56 tests pass.
domuk merged commit a605b877df into main 2026-08-16 22:57:02 +01:00
domuk deleted branch fix/core-permission-format 2026-08-16 22:57:03 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
domuk/Ion-HomeLab!2
No description provided.