Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
fbb8426
docs(audit): full security and code-health audit report
hasan-ismail Aug 1, 2026
6b24d5f
test(security): add test + lint infrastructure for the safety gate [A…
hasan-ismail Aug 1, 2026
dca63c3
fix(security): reject path traversal that defeats the commit-SHA pin …
hasan-ismail Aug 1, 2026
defc437
fix(security): reject external and renamed networks in compose [APPS-…
hasan-ismail Aug 1, 2026
592f8b3
fix(security): enforce the documented label ban and close shape gaps …
hasan-ismail Aug 1, 2026
c0da624
fix(security): remove ReDoS and bound every remote fetch [APPS-007]
hasan-ismail Aug 1, 2026
192e35c
fix(ci): use npm ci, pin all actions to SHAs, gate publish on tests […
hasan-ismail Aug 1, 2026
5389c1a
fix(security): validate untrusted manifest fields at the catalog boun…
hasan-ismail Aug 1, 2026
f030efb
fix(security): close .gitignore gaps [APPS-015]
hasan-ismail Aug 1, 2026
82cfbdf
docs(audit): scope the branch to the catalog and correct finding stat…
hasan-ismail Aug 1, 2026
bf0b86e
docs(audit): correct APPS-003 - template defect, not OpenMasjidDisplay's
hasan-ismail Aug 1, 2026
64e8b1d
docs: stop CLAUDE.md describing examples/ as working software
hasan-ismail Aug 1, 2026
14eb844
chore(ci): add Dependabot so the SHA pins stay maintained [APPS-006]
hasan-ismail Aug 1, 2026
8a31fb0
docs(audit): add REMEDIATION, ACTION_REQUIRED and findings.json
hasan-ismail Aug 1, 2026
9c8bcfd
ci: run lint and tests on pull requests, before merge [APPS-020]
hasan-ismail Aug 1, 2026
7ecae8b
fix(test): make npm test work on Node 20, not just Node 24 [APPS-020]
hasan-ismail Aug 1, 2026
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
38 changes: 38 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (C) 2026 OpenMasjid-Solutions
#
# Every action in this repo is pinned to a full commit SHA (see build-catalog.yml
# and cla.yml). That stops a moved tag from reaching the workflow that publishes
# catalog.json — but it also freezes the version, so a pin left alone becomes a
# stale pin with known bugs. Dependabot keeps the pins moving: it opens a PR that
# updates the SHA and the trailing "# vN" comment together, which a human reviews.
#
# Scope is deliberately narrow. It watches this repo's own tooling only:
# - github-actions: the workflows the catalog runs on
# - npm: the single runtime dependency (yaml)
# It does NOT watch examples/ — that is illustrative scaffolding, not software
# this repo maintains (CLAUDE.md §1), and its workflows never run from here.
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: 'chore(ci)'
labels: [dependencies, github-actions]

- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
commit-message:
prefix: 'chore(deps)'
labels: [dependencies]
# A major bump to the one dependency the build parses untrusted YAML with is a
# review decision, not an automatic one.
ignore:
- dependency-name: 'yaml'
update-types: [version-update:semver-major]
23 changes: 20 additions & 3 deletions .github/workflows/build-catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,32 @@ jobs:
# scoped to Contents: write on this repo); checkout persists it so the later
# `git push` uses it. Falls back to the default token when the secret is unset
# (the push then fails loudly, as before, and the catalog is published manually).
- uses: actions/checkout@v4
# Actions are pinned to full commit SHAs, not mutable tags. This job holds a
# branch-protection-bypassing PAT and publishes the file every masjid installs
# from, so a moved tag on any action used here would be a direct supply-chain
# route into production. Same convention as cla.yml. Keep the version in the
# trailing comment when bumping. (APPS-006)
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
token: ${{ secrets.CATALOG_PUSH_TOKEN || github.token }}

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: npm

- run: npm install
# `npm ci` not `npm install`: install is free to resolve a newer version
# inside the "^2.5.0" range and to rewrite the lockfile, so this unattended
# job could pull an unreviewed dependency and then publish with it. ci
# installs exactly what package-lock.json pins. (APPS-005)
- run: npm ci

# Gate the publish on the checks. catalog.json on main IS the production
# artifact, so the safety gate (scripts/validate-compose.mjs) must be proven
# working BEFORE anything is regenerated and pushed. (APPS-020)
- run: npm run lint

- run: npm test

- run: npm run build

Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (C) 2026 OpenMasjid-Solutions
name: Checks

# Run the catalog's own checks on every pull request, BEFORE anything is merged.
#
# build-catalog.yml also runs lint and tests, but it only triggers on push to
# `main` (plus cron/dispatch) — and on `main` the regenerated catalog.json is
# published immediately, because the platform fetches that raw file with no
# deploy gate. Catching a problem there means catching it after it is live.
# This workflow moves the gate in front of the merge.
#
# Deliberately minimal permissions: it reads the code and reports a status. It
# never needs a token that can write to the repo or publish anything.
on:
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: checks-${{ github.ref }}
cancel-in-progress: true

jobs:
checks:
runs-on: ubuntu-latest
steps:
# Pinned to a SHA like every other action here — see build-catalog.yml.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: npm

- run: npm ci

# Parses every script, enforces the SPDX header CLAUDE.md §10 mandates, and
# asserts catalog.json still matches the platform contract in CLAUDE.md §2.
- run: npm run lint

- run: npm test

# `npm audit` on the committed lockfile. Advisory: it is allowed to fail the
# step without failing the job, so a fresh advisory in a transitive package
# cannot block an unrelated catalog PR — but it is visible in the log.
- name: npm audit (advisory)
continue-on-error: true
run: npm audit --audit-level=moderate

# NOTE: `npm run build` is intentionally NOT run here. It fetches every
# listed app repo from GitHub, so it needs network access and is subject to
# rate limiting; more importantly it rewrites catalog.json, which must only
# ever be regenerated by build-catalog.yml on `main`. Registry changes are
# validated by that workflow after merge.
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,31 @@ node_modules/
*.log
.DS_Store
Thumbs.db

# Environment files. `.env` alone missed the variants tools actually create,
# and the platform writes each install's answers to a .env — never commit one.
.env
.env.*
!.env.example

# Key, certificate and credential material. Nothing of the sort has ever been
# committed here (the audit content-scanned all 475 commits); this is to keep it
# that way, and app authors copy this file as their starting point.
*.pem
*.key
*.p12
*.pfx
*.crt
*.cer
*.der
id_rsa
id_ed25519
*.jks
*.keystore

# Registry auth tokens land here.
.npmrc

# Local build/test scratch.
coverage/
*.tsbuildinfo
43 changes: 38 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ populate its App Store.
- `registry.yaml` — the hand-edited list of app repositories to include.
- `scripts/build-catalog.mjs` — fetches each listed repo and generates `catalog.json`.
- `catalog.json` — **generated**; the only file the platform reads.
- `examples/` — complete, working **reference apps** you copy into a new repo to start. They are
**not** part of the catalog (the registry is); they are templates/documentation only.
- `examples/` — **illustrative scaffolding only.** Skeletons showing the *shape* an app repo must
have (`manifest.yaml`, `docker-compose.yml`, a Dockerfile, an entrypoint that turns settings into
runtime config). They are **documentation, not software this repo maintains**: they are **not** in
the catalog, **not** built, **not** tested, **not** released, and **not** run by any masjid. Copy
one into a new repo as a starting point; from that moment it is *your* app's code and it lives and
is maintained in *your* repo. Do not treat them as a working product, and do not "fix" them as
part of catalog work — see §15.
- `docs/BUILDING_AN_APP.md` — the hands-on guide for building a compatible app repo.
- `docs/DESIGN.md` — the full UI/UX design language (Sakīna Glass tokens, motion, dock, components)
every app should match so it looks native to OpenMasjidOS.
Expand All @@ -43,6 +48,13 @@ Live app source, Dockerfiles for shipped apps, or per-app image CI. **Those live
repo.** Do not add an `apps/` folder of real apps here, and do not reintroduce a per-app image
build workflow into this repo.

It also does not own **app behaviour or app correctness**. Prayer-time calculation, Hijri dates,
Qibla, Zakat and donation maths, UI, accessibility and RTL are each app's responsibility, in each
app's own repository. For example, the prayer clocks masjids actually run come from the **`display`**
app (`OpenMasjid-Solutions/OpenMasjidDisplay`), which calculates them itself — nothing under
`examples/` is that engine, and changing `examples/` cannot fix or break it. If you find a
correctness bug in an app, it is a bug **in that app's repo**; open it there.

---

## 2. The platform contract — DO NOT BREAK THIS
Expand Down Expand Up @@ -127,7 +139,9 @@ apps:
## 4. Requirements for an app repository (READ THIS if you are building an app)

> **For other agents/authors:** an app you build must meet *all* of the following to be listed and
> to install cleanly. The fastest path is to copy `examples/<an-app>/` into a new repo and adapt it.
> to install cleanly. A quick way to start is to copy the layout of `examples/<an-app>/` into a new
> repo — but treat it as a skeleton to build on, not finished software (§1): once copied, the code is
> yours and it is maintained in your repo, and it is your job to make it meet §11.
> A step-by-step version with copy-paste templates is in **`docs/BUILDING_AN_APP.md`**.

**A. The repository**
Expand Down Expand Up @@ -355,6 +369,11 @@ namespace/cap/device/mount checks. When the platform adds a compose check, add t
---

## 11. Quality bar for apps

> **Applies to an app's own repository, not to this tree.** Everything in this section is normative
> for the repo where the app lives — including anything under `examples/`, once you have copied it
> into your own repo. Nothing here is a to-do list for work in OpenMasjidAPPS (§15).

- **Match the OpenMasjidOS design language** — see **[`docs/DESIGN.md`](docs/DESIGN.md)**: the Sakīna
Glass material, color tokens (dark default + light), spring motion, the dock, components, RTL, and
voice. Prefer inheriting the live appearance via the Fabric (§7b) so the app tracks the dashboard;
Expand All @@ -380,8 +399,8 @@ OpenMasjidAPPS/
├── scripts/build-catalog.mjs # registry → catalog.json (fetches app repos)
├── docs/BUILDING_AN_APP.md # hands-on guide for building a compatible app repo
├── docs/DESIGN.md # the full UI/UX design language every app should match
├── examples/ # complete reference apps to copy into a new repo (NOT catalogued)
│ ├── prayer-times-display/
├── examples/ # illustrative scaffolding — NOT catalogued, built, tested or
│ ├── prayer-times-display/ # maintained. Out of scope for work here; see §1 and §15.
│ └── announcements-board/
└── .github/workflows/build-catalog.yml
```
Expand All @@ -398,12 +417,26 @@ npm install && npm run build # regenerate catalog.json from registry.yaml (nee
`catalog.json` whose **shape matches §2** (the platform is unaffected); and CI is green.
- **An app** (in its own repo) is done when it meets every requirement in §4 and **installs and
opens cleanly on a real OpenMasjidOS instance** with only the settings collected at install time.
This bar is assessed in the app's repo. It is **not** a bar `examples/` has to meet, and a catalog
change is never blocked on it.

## 15. Working agreement for Claude (in this repo)
- Read this file first, every session. **§2 (platform contract) is a hard constraint** — never
change the shape of `catalog.json` here; that would break the platform.
- This repo is **catalog-only**. Don't add real app source here. New apps go in their own repos and
are added to `registry.yaml`.
- **Do not modify app source in this repo — that includes `examples/`.** Work here is limited to
`registry.yaml`, `scripts/`, `.github/workflows/`, `docs/`, and the repo's own config. `examples/`
is illustrative scaffolding (§1), so it is **out of scope for reviews, audits, refactors, test
coverage and bug fixes**. If something under `examples/` is wrong or incomplete, say so and stop;
don't fix it as a side quest. Change it only when the user explicitly asks for a change to the
example scaffolding itself.
- **App correctness is never this repo's bug.** Prayer times, Hijri dates, Qibla, Zakat and donation
maths, UI/RTL/accessibility, and per-app security all belong to the app's own repository — for
prayer times that is `OpenMasjidDisplay`, not `examples/`. Report such a finding as a cross-repo
item for that repo; do not "fix" it here, because fixing it here fixes nothing for any masjid.
Never assert a bug in an app repo you have not actually read: the build fetches only each app's
`manifest.yaml` and `docker-compose.yml`, so its application code is not visible from here.
- Never hand-edit `catalog.json`; change `registry.yaml` (or an app repo) and run the build.
- Keep `id` == app's manifest id == registry id, kebab-case, matching `^[a-z0-9][a-z0-9-]{0,79}$`.
- Never copy Umbrel/CasaOS definitions or assets (§10). Author fresh.
Expand Down
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,17 @@ backwards-compatible, and never carries masjid data. See

## `examples/`

Complete, working **reference apps** (`prayer-times-display`, `announcements-board`) you copy into a
new repo to start. They are templates/documentation — they are **not** part of the catalog (the
registry is).
**Illustrative scaffolding** (`prayer-times-display`, `announcements-board`) showing the *shape* an
app repo needs: `manifest.yaml`, `docker-compose.yml`, a Dockerfile, and an entrypoint that turns
install settings into runtime config.

They are documentation, not software this repo maintains — **not** in the catalog, **not** built,
tested or released, and **not** run by any masjid. Copy one as a starting point; from then on the
code is yours, lives in your repo, and it is your job to make it meet the quality bar in
[`CLAUDE.md` §11](CLAUDE.md). App behaviour and correctness (prayer times, Hijri dates, Zakat maths,
RTL) belong to each app's own repository — the prayer clocks masjids actually run come from the
`display` app, [OpenMasjidDisplay](https://github.com/OpenMasjid-Solutions/OpenMasjidDisplay), not
from anything here.

## Maintainers — catalog auto-publish (`CATALOG_PUSH_TOKEN`)

Expand Down
Loading
Loading