Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
176 commits
Select commit Hold shift + click to select a range
68404a0
chore: stop tracking AI-assistant config and internal docs
hakeemRash Jul 7, 2026
fca4231
Merge pull request #6 from AlfaCode-Team/chore/remove-ai-docs
hakeemRash Jul 7, 2026
fe07980
feat(cli): show Sentinel banner on bare hkm and hkm help
hakeemRash Jul 7, 2026
58fe2c5
Merge pull request #7 from AlfaCode-Team/feat/banner-on-help
hakeemRash Jul 7, 2026
10c8f37
feat(upgrade): auto-download and install updates per OS
hakeemRash Jul 7, 2026
562a344
Merge pull request #8 from AlfaCode-Team/feat/hkm-upgrade-autoinstall
hakeemRash Jul 7, 2026
bacb6e8
fix(cli): self-locate installed kernel + real hkm-config + load confi…
hakeemRash Jul 7, 2026
7a01a63
Merge pull request #9 from AlfaCode-Team/fix/kernel-self-location
hakeemRash Jul 7, 2026
f367603
fix(templates): move templates out of tools/ so they ship in the kernel
hakeemRash Jul 7, 2026
2e05a8f
Merge pull request #10 from AlfaCode-Team/fix/ship-templates
hakeemRash Jul 7, 2026
e60838b
chore(release): v1.0.3
hakeemRash Jul 7, 2026
dfd65fa
Merge pull request #11 from AlfaCode-Team/release/v1.0.3
hakeemRash Jul 7, 2026
1b085b4
chore(cli): empty the committed project registry + auto-clear hook + …
hakeemRash Jul 7, 2026
e28c233
Merge pull request #12 from AlfaCode-Team/chore/registry-privacy-and-…
hakeemRash Jul 7, 2026
a1531b3
chore: refresh contributor statistics
hakeemRash Jul 7, 2026
ead0a18
feat(userdata): HKM_USERDATA_DIR for persistent registry across updates
hakeemRash Jul 7, 2026
feb8e60
Merge pull request #13 from AlfaCode-Team/feat/userdata-dir
hakeemRash Jul 7, 2026
6bf2b11
feat(config): hkm-config sets up the full environment (kernel + userd…
hakeemRash Jul 7, 2026
54cd260
Merge pull request #14 from AlfaCode-Team/feat/hkm-config-full-setup
hakeemRash Jul 7, 2026
3a4f9e0
security: harden scaffolding defaults
hakeemRash Jul 7, 2026
639c468
Merge pull request #15 from AlfaCode-Team/sec/harden-scaffolding
hakeemRash Jul 7, 2026
2559aa1
chore(release): v1.0.4
hakeemRash Jul 7, 2026
e0f907a
Merge pull request #16 from AlfaCode-Team/release/v1.0.4
hakeemRash Jul 7, 2026
95110aa
feat(scaffold): ship an Apache vhost sample alongside nginx
hakeemRash Jul 7, 2026
775df28
Merge pull request #17 from AlfaCode-Team/feat/apache-config-sample
hakeemRash Jul 7, 2026
98cd91b
chore(release): v1.0.5
hakeemRash Jul 8, 2026
badf5b1
docs: rewrite README for the native hkm CLI; fix broken doc links
hakeemRash Jul 8, 2026
0ba721e
Merge pull request #19 from AlfaCode-Team/docs/refresh-readme
hakeemRash Jul 8, 2026
a15ae26
feat(routes,cli): project routePolicy.disable + hkm --dev environment
hakeemRash Jul 9, 2026
689c983
chore(release): v1.0.6
hakeemRash Jul 9, 2026
65be3c4
docs: document hkm --dev + HKM_DEV_HOME in README and CLI usage guide
hakeemRash Jul 9, 2026
edc79a2
ci: bump actions to v5 (checkout, upload/download-artifact) for Node 24
hakeemRash Jul 9, 2026
d35a689
feat(tools): hkm plugins upgrade — split-safe project upgrade
hakeemRash Jul 10, 2026
dca5f0b
chore(release): v1.0.7
hakeemRash Jul 10, 2026
d5a8ff6
test(feedback): fix FeedbackServiceTest namespace after User→Feedback…
hakeemRash Jul 10, 2026
132c9f7
refactor(plugins): complete Feedback/Mail/Validation split
hakeemRash Jul 10, 2026
a734b12
Merge pull request #22 from AlfaCode-Team/feat/complete-feedback-mail…
hakeemRash Jul 10, 2026
f5e54b8
chore(githooks): strip AI-authored trailers in commit-msg hook
hakeemRash Jul 11, 2026
d7c4044
docs: rewrite README as a full framework guide (concepts, lifecycle, …
hakeemRash Jul 11, 2026
eed6473
docs: rewrite README as a full framework guide (concepts, lifecycle, …
hakeemRash Jul 11, 2026
a7c468f
chore: stop tracking tools/zig-out build output (rebuilt by zig build…
hakeemRash Jul 11, 2026
6c4048a
fix(cli,tenancy): resolve tenant:migrate command collision + project-…
hakeemRash Jul 11, 2026
a79694d
feat(audit,auth,social,tenancy,cli): audit plugin, device/mobile auth…
hakeemRash Jul 13, 2026
72a99a1
chore(release): v1.0.9
hakeemRash Jul 13, 2026
c205543
feat(auth,session,tenancy,user): display identity, previous-page logi…
hakeemRash Jul 14, 2026
479c17c
chore(release): v1.0.10
hakeemRash Jul 14, 2026
2e865a6
feat(kernel,tenancy,pageflow,auth): project essentials, strict tenant…
hakeemRash Jul 16, 2026
11b5233
chore(release): v1.0.11
hakeemRash Jul 16, 2026
7206d30
fix(security-filters): RequireAuthStage only redirects real page navi…
hakeemRash Jul 16, 2026
de23222
build(bundle): pin bundle dependencies to the PHP 8.4 series
Alshatri Jul 16, 2026
82bc557
build(bundle): pin bundle dependencies to the PHP 8.4 series
Alshatri Jul 16, 2026
7f3888a
Merge branch 'main'
hakeemRash Jul 16, 2026
195b7e7
ci(release): auto-tag new CHANGELOG version on merge to main -> trigg…
hakeemRash Jul 16, 2026
d86341b
ci(release): auto-release on merge to main via workflow_call (no PAT)…
hakeemRash Jul 16, 2026
1218ab1
docs(readme): document master->main branch model and automatic CHANGE…
hakeemRash Jul 16, 2026
a68ce88
ci(security): CODEOWNERS + main branch protection script (required re…
hakeemRash Jul 16, 2026
a796202
feat(edge): host-aware web-server config plugin (nginx SNI stream spl…
hakeemRash Jul 16, 2026
dc335b4
feat(edge): classify .local/.test as local domains — exclude from ser…
hakeemRash Jul 17, 2026
9af3885
fix(edge): resolve project registry from global kernel home (not proj…
hakeemRash Jul 17, 2026
87ccd90
feat(edge): project-aware config — per-project vhosts (docroot app/pu…
hakeemRash Jul 17, 2026
c84409c
feat(edge): default commands to the CURRENT project (base_path/proj.j…
hakeemRash Jul 17, 2026
0533c81
feat(edge): auto-resolve PHP-FPM socket to match the CLI PHP version …
hakeemRash Jul 17, 2026
f2bef6e
feat(userconfig): resolve config path for non-root sudo users
hakeemRash Jul 17, 2026
5514c5c
Adopt master→main branch model, automated releases, and main branch p…
hakeemRash Jul 17, 2026
d0a99cb
Adopt master→main branch model, automated releases, and main branch p…
hakeemRash Jul 17, 2026
9aad6f6
feat(edge): enhance hosts management with dev mode checks and force o…
hakeemRash Jul 17, 2026
ad7ec07
chore(release): v1.0.13 — Edge plugin, PSP_PROJECTS_DIR export, front…
hakeemRash Jul 17, 2026
e6bf46d
Merge remote-tracking branch 'origin/main'
hakeemRash Jul 17, 2026
da9573c
chore(release): bump to v1.0.14 (v1.0.13 tag already exists from an e…
hakeemRash Jul 17, 2026
2960acf
chore(release): reclaim v1.0.13 (stale mis-tagged v1.0.13 release/tag…
hakeemRash Jul 17, 2026
eb31d1e
Release v1.0.13 — Edge plugin + launcher/frontend fixes (#24)
hakeemRash Jul 17, 2026
1c0aeb1
Release v1.0.13 — Edge plugin + launcher/frontend fixes (#24)
hakeemRash Jul 17, 2026
e37a293
chore(release): v1.0.14
hakeemRash Jul 17, 2026
59f925a
chore: align CHANGELOG to released v1.0.13 (drop phantom 1.0.14)
hakeemRash Jul 17, 2026
310c874
fix(edge): serve local .local/.test domains in dev mode (--dev)
hakeemRash Jul 17, 2026
ef0027f
Merge origin/main into master (squash-merge of #24; master already co…
hakeemRash Jul 17, 2026
170f48c
Release v1.0.15 — Edge serves local domains in dev (#25)
hakeemRash Jul 17, 2026
d9ee5d7
Release v1.0.15 — Edge serves local domains in dev (#25)
hakeemRash Jul 17, 2026
4436575
Merge branch 'main' of github.com:AlfaCode-Team/php-service-platform
Alshatri Jul 18, 2026
53696ad
Merge branch 'master' of github.com:AlfaCode-Team/php-service-platform
Alshatri Jul 18, 2026
57b5831
feat(edge): TLS modes, APP_ENV cache profiles and OpenSwoole runtime …
hakeemRash Jul 18, 2026
2933630
feat(edge): TLS modes, APP_ENV cache profiles and OpenSwoole runtime …
hakeemRash Jul 18, 2026
0a3f640
Merge origin/main into master (PR #26: Edge TLS modes, APP_ENV cache …
hakeemRash Jul 18, 2026
af463d0
feat(tenancy): add control plane support and enhance tenant migration…
hakeemRash Jul 20, 2026
f704871
feat(cli): hkm discover — find and register projects on disk
hakeemRash Jul 20, 2026
92b575e
Release v1.0.17 — hkm discover: find & register projects on disk (#27)
hakeemRash Jul 20, 2026
fffc4d8
feat(cli): plugins recover + discover restores runtime dirs
hakeemRash Jul 20, 2026
4a33ef4
Merge remote-tracking branch 'origin/main'
hakeemRash Jul 20, 2026
b9fb1dd
Release v1.0.18 — plugins recover + discover restores runtime dirs (#28)
hakeemRash Jul 20, 2026
d4b58cb
feat(edge): stream reuse/merge, force strategy, security hardening; r…
hakeemRash Jul 21, 2026
f804f42
Merge remote-tracking branch 'origin/main'
hakeemRash Jul 21, 2026
11f319d
test(edge): make nginx -t check portable across nginx versions
hakeemRash Jul 21, 2026
8875546
test(edge): make nginx -t syntax check runnable as non-root in CI
hakeemRash Jul 21, 2026
624dd4f
chore: auto-wire git hooks to strip AI co-author trailers
hakeemRash Jul 21, 2026
b921a72
chore: drop pulse-engine + module-template modules, patch guzzle, wir…
hakeemRash Jul 21, 2026
b5b2d0e
feat: add issue and pull request templates, contributing guide, secur…
hakeemRash Jul 21, 2026
6d8203a
feat: add issue templates for documentation and plugin proposals
hakeemRash Jul 21, 2026
ed45d09
Merge branch 'main' of github.com:AlfaCode-Team/php-service-platform
hakeemRash Jul 21, 2026
d1c5543
Merge branch 'main' of github.com:AlfaCode-Team/php-service-platform
hakeemRash Jul 21, 2026
6b397df
Merge branch 'main' of github.com:AlfaCode-Team/php-service-platform
hakeemRash Jul 21, 2026
5537430
ci: add CodeQL (JS/TS + Actions) and PHP analysis (composer audit, Se…
hakeemRash Jul 21, 2026
6b846c9
ci: drop custom CodeQL workflow — repo uses CodeQL default setup (avo…
hakeemRash Jul 21, 2026
e7818c3
ci(phpstan): add level-5 config + baseline, make analysis a blocking …
hakeemRash Jul 21, 2026
320e924
fix: repair MigrateListCommand parent + OutboxWriter port contract
hakeemRash Jul 21, 2026
fe4bfc2
ci: fix PHPStan (ignore optional Swoole/OpenSwoole coroutine calls) +…
hakeemRash Jul 21, 2026
248d9f9
Merge branch 'main' of github.com:AlfaCode-Team/php-service-platform
hakeemRash Jul 21, 2026
b1d0451
chore(oss): open-source readiness — license, composer metadata, .env.…
hakeemRash Jul 21, 2026
45ce8a5
chore: add craftdevscommunity as code owner (required PR reviewers fo…
hakeemRash Jul 21, 2026
1da40e9
Refactor Pageflow stages and consolidate functionality
hakeemRash Jul 22, 2026
5b6b2fc
Depend on alfacode-team/http ^1.0 (dev-master in monorepo, v1.0.0 for…
hakeemRash Jul 22, 2026
36852f6
feat(module): add `hkm module` command for managing first-party kerne…
hakeemRash Jul 22, 2026
7573b25
chore: update subproject commit for http module
hakeemRash Jul 22, 2026
caad9ea
Point http submodule at latest master (dev-master, v1.0.0 release for…
hakeemRash Jul 22, 2026
b13cfa3
refactor(PageflowResponderTest): consolidate stage classes into a sin…
hakeemRash Jul 22, 2026
40e2fef
Refactor code structure for improved readability and maintainability
hakeemRash Jul 22, 2026
81e1194
Merge branch 'main' into master
hakeemRash Jul 22, 2026
cde63b8
docs: update README to enhance clarity and structure, add new sections
hakeemRash Jul 22, 2026
400bce5
Merge branch 'master' of github.com:AlfaCode-Team/hkm-kernel
hakeemRash Jul 22, 2026
9cd205d
Merge #36; rebrand to HKM Kernel, publish docs/guides, README rewrite…
hakeemRash Jul 22, 2026
bfff769
Merge branch 'main' of github.com:AlfaCode-Team/hkm-kernel
hakeemRash Jul 22, 2026
4f4122f
feat: add MailDemoController for testing routes and implement preview…
hakeemRash Jul 27, 2026
471b4e1
feat: add OAuth2 consent and simulation pages
hakeemRash Jul 27, 2026
c461c22
Merge branch 'main' of github.com:AlfaCode-Team/hkm-kernel
hakeemRash Jul 27, 2026
e708060
feat: update OAuth2 module routes and remove mobile authentication en…
hakeemRash Jul 31, 2026
03f60ad
refactor: update references from "Sentinel" to "HKM" in upgrade and b…
Alshatri Jul 31, 2026
7289e61
chore: remove unused submodules for module-template and pulse-engine
Alshatri Jul 31, 2026
af5f6b8
Merge branch 'master' of github.com:AlfaCode-Team/hkm-kernel
Alshatri Jul 31, 2026
13a5a31
feat(kernel): add config repository, logger port, locks, typed routes…
Alshatri Aug 6, 2026
2c490cc
feat(plugins): adopt the new kernel ports; add Logger plugin
Alshatri Aug 6, 2026
53069b1
docs: reconcile drift and document the kernel additions
Alshatri Aug 6, 2026
b16e0c3
fix(tests): repair three OAuth2 fakes drifted from their interfaces
Alshatri Aug 6, 2026
8e2db54
refactor!: decouple plugins from the kernel into standalone packages
Alshatri Aug 6, 2026
db63dae
refactor!: kernel depends on no plugins; plugin tests move to their o…
Alshatri Aug 6, 2026
a076032
fix(deps): pin phpshots/bind-it instead of accepting any version
Alshatri Aug 6, 2026
c4f14c8
feat(boot): compile a translation catalogue cascade so plugins can sh…
Alshatri Aug 6, 2026
c718957
fix(tools): make defers run at exit; wire the memory inspector; repai…
Alshatri Aug 6, 2026
bdbbf34
feat(tools): install, update and lock plugins from their git remotes
Alshatri Aug 6, 2026
86164b6
fix(tools): a git-describe build must not sort below the tag it came …
Alshatri Aug 6, 2026
b97a661
feat(tools): stamp the build version into composer.json; fix the upgr…
Alshatri Aug 6, 2026
6c3068e
feat(tools): hkm upgrade --local — install the local checkout over th…
Alshatri Aug 6, 2026
7e2911c
fix(tools): a pre-release tag must not be offered as the latest update
Alshatri Aug 7, 2026
406f875
docs: changelog for 1.1.0-dev.1
Alshatri Aug 7, 2026
7390e0b
fix: PHPStan could not run at all; clear the kernel's own findings
Alshatri Aug 7, 2026
44061af
fix(tools): `**` array-repeat was removed in Zig 0.17 — the release b…
Alshatri Aug 7, 2026
fe3c478
fix(ci): releases published with no binaries attached
Alshatri Aug 7, 2026
a660a64
docs: changelog for 1.1.0-dev.2
Alshatri Aug 7, 2026
df80fdc
fix(ci): auto-release skipped pre-release versions and picked an olde…
Alshatri Aug 7, 2026
b8ac839
fix: PHPStan green — fix the real finding, scope out plugin-bound sca…
Alshatri Aug 7, 2026
d868f5c
docs: changelog for 1.1.0-dev.3
Alshatri Aug 7, 2026
d51b215
fix(tools): stamping an invalid version broke composer install on eve…
Alshatri Aug 7, 2026
a296b7f
docs: release 1.1.0-beta.1 — the first installable pre-release
Alshatri Aug 7, 2026
648db50
Merge branch 'main' into master
hakeemRash Aug 7, 2026
a29ac08
fix(tools): restore the composer version fix lost in the main->master…
Alshatri Aug 7, 2026
b725988
Merge branch 'main' of github.com:AlfaCode-Team/hkm-kernel
Alshatri Aug 7, 2026
85bfcc6
fix(.gitignore): add 'var' to ignored files
Alshatri Aug 7, 2026
29dccfb
fix(tools): a new project could not boot — hkm new never installed it…
Alshatri Aug 8, 2026
38fb056
feat(routing): groups, domain grouping, safer parameters and a boot c…
Alshatri Aug 11, 2026
04706d9
chore: release 1.2.0
Alshatri Aug 11, 2026
33cc616
fix(release): point modules/let-migrate back at its published commit
Alshatri Aug 11, 2026
50a1d26
feat: add plugin domains and store management
Alshatri Aug 11, 2026
7fe1de2
fix(release): drop the version field from the checked-in composer.json
Alshatri Aug 11, 2026
e2ecaae
fix(release): actually pin let-migrate to its published commit
Alshatri Aug 11, 2026
546d625
fix(ci,stamp): stop duplicate PR checks and reject a version that cor…
Alshatri Aug 11, 2026
932b512
Merge branch 'main' of github.com:AlfaCode-Team/hkm-kernel
hakeemRash Aug 12, 2026
fa4c9d2
chore: update subproject commits for http and php-io-cli modules
hakeemRash Aug 12, 2026
b0ef872
Merge branch 'master' of github.com:AlfaCode-Team/hkm-kernel
hakeemRash Aug 12, 2026
6c7de55
feat(routing): accept a LIST of domains at every level
Alshatri Aug 12, 2026
f5ee7af
feat(plugins): seed a plugin's declared env into .env on enable
Alshatri Aug 12, 2026
1cb12b9
feat(install): a user-local tarball install that needs no root
Alshatri Aug 12, 2026
824b1b5
feat(template): wire the scaffold up to @pageflow/admin
Alshatri Aug 12, 2026
99ffa44
fix(release): pin php-io-cli back to its last loadable commit
Alshatri Aug 12, 2026
c92d55f
chore: release 1.3.0
Alshatri Aug 12, 2026
1474f2d
chore: release 1.3.1
Alshatri Aug 12, 2026
1f6da00
update
hakeemRash Aug 13, 2026
34abb2c
feat: initialize projects configuration with shop, hkmcode, and hkmvo…
Alshatri Aug 14, 2026
299a271
fix(cli): scope-aware install, upgrade and version reporting
hakeemRash Aug 17, 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
8 changes: 7 additions & 1 deletion .github/workflows/php-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ name: PHP Analysis
# • PHPStan — type/static analysis (non-blocking until a baseline lands)

"on":
# `push` is limited to main — the same shape ci.yml uses. Listing master here
# too made every master->main PR run this workflow TWICE: the push event fires
# for refs/heads/master and the pull_request event for refs/pull/N/merge, and
# because the concurrency group is keyed on the ref, the two never collide.
# That is where the duplicate "composer audit", "PHPStan" and "Semgrep" checks
# on a PR came from. Changes to master still get analysed — through the PR.
push:
branches: [main, master]
branches: [main]
pull_request:
branches: [main, master]
schedule:
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ jobs:
# macOS universal binary is assembled with llvm-lipo. This needs only ONE
# self-hosted Zig toolchain (Linux) and no macOS runner.

# ── Linux: .deb (amd64) ────────────────────────────────────────────────────
# ── Linux: portable tarball (default install) + .deb (system-wide) ────────
build-linux:
name: Build Linux .deb
name: Build Linux tarball + .deb
needs: test
runs-on: ubuntu-22.04
steps:
Expand All @@ -63,8 +63,16 @@ jobs:
with: { php-version: "8.4", tools: composer }
- name: Bundle (linux)
run: VERSION="${RELEASE_VERSION:-${GITHUB_REF_NAME#v}}" ./tools/bundle.sh linux
# TWO artifacts. The tarball is the DEFAULT install path — user-local,
# no root (tools/install.sh). The .deb is for multi-user machines and CI
# images where a system-wide install and apt-managed PHP are the point.
- uses: actions/upload-artifact@v5
with: { name: linux-tarball, path: dist/*linux*.tar.gz }
- uses: actions/upload-artifact@v5
with: { name: linux-deb, path: dist/*.deb }
# Published alongside the assets so `curl | sh` works without a checkout.
- uses: actions/upload-artifact@v5
with: { name: installer, path: tools/install.sh }

# ── Windows: .zip (x86_64, cross-compiled) ────────────────────────────────
build-windows:
Expand Down Expand Up @@ -135,9 +143,11 @@ jobs:
# explicitly; on a tag push this matches GITHUB_REF_NAME anyway.
tag_name: v${{ steps.notes.outputs.version }}
files: |
artifacts/linux-tarball/*.tar.gz
artifacts/linux-deb/*.deb
artifacts/windows-zip/*.zip
artifacts/macos-app/*.tar.gz
artifacts/installer/install.sh
# Curated section (if present) goes first; GitHub appends the
# auto-generated "What's Changed" / contributors below it.
body_path: ${{ steps.notes.outputs.has_notes == 'true' && 'release-body.md' || '' }}
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ tools/zig-out/
# ...but publish the curated, reader-facing guides (docs/ai-context stays ignored):
!/docs/guides/

/tmp/

# ── COMPOSER ──────────────────────────────────────────────────
/vendor/
#(Uncomment if you want to ignore lock file for the library)
composer.lock
*.bak

/var/

# ── TESTING & COVERAGE ────────────────────────────────────────
/.phpunit.cache/
Expand Down Expand Up @@ -84,3 +85,5 @@ ehthumbs.db
*.swo
*~
**/.zig-cache

/userdata/
244 changes: 244 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,250 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.2] - 2026-08-17

Fixes a class of failure that made installing or upgrading on a machine with an
existing install appear to do nothing. A machine can hold BOTH a system install
(`.deb` → `/opt/hkm-kernel` + `/usr/bin`) and a user install (tarball →
`~/.local`); the CLI did not model that, and every symptom below followed from
the same gap.

**If you are upgrading from 1.3.1 or earlier, the old launcher cannot install
the user scope.** Install it from the release instead — the fixed `hkm upgrade`
takes over from there:

```sh
curl -fsSL https://github.com/AlfaCode-Team/hkm-kernel/releases/latest/download/install.sh | sh
hkm version # shows every install and which one your PATH runs
```

### Added
- **`hkm version` reports every install on the machine**, not just the launcher's
own compile-time stamp: the kernel version in each scope (read from that
kernel's `composer.json`), the launcher serving it and the version IT was built
as, and an arrow on the one this invocation resolves. It also names the states
that make a later "my upgrade did nothing" report inevitable — another `hkm`
earlier on `PATH`, a kernel with no `vendor/`, a stale config pin. `hkm
--version` is unchanged and still prints one line for scripts.
- **`hkm upgrade --user` / `--system`** to force a scope. Without either, the
target is chosen from privilege — root → system, otherwise → user — so
`sudo hkm upgrade` and `hkm upgrade` are two predictable commands rather than
one command whose target depends on machine state.
- **`hkm-config unset <KEY>`**, for clearing a stale `HKM_KERNEL_HOME`.
- `hkm doctor` gained an **Installs** table: both scopes, their versions and
whether each has resolved dependencies.

### Changed
- **Kernel resolution ranks sources by how specific they are to the invocation**
(`tools/src/lib/kernel.zig`): an exported `HKM_CLI_PATH` / `HKM_KERNEL_HOME`,
then self-location relative to the launcher's own binary, then a
`config.env` pin, then `/opt/hkm-kernel`. The pin was previously checked
first. It still applies wherever self-location genuinely fails — a custom
prefix — but no longer overrides an install sitting next to the binary.
A launcher in a system bin directory (`/usr/bin`) claims `/opt/hkm-kernel` at
the self-location step, since no relative probe can reach it from there.
- **`hkm upgrade --user` installs to `~/.local/lib/hkm-kernel`**, matching
`install.sh`, instead of `~/.local/share/hkm/kernel`. The old path sits outside
every self-location probe, so it could only ever be reached through a
machine-wide pin — which is what created the cross-scope hijack below. An
install left at the old location is detected and reported, not silently used.
- **`install.sh` removes a redundant or superseded `HKM_KERNEL_HOME` pin**
rather than repointing it. A repointed pin is still read by every launcher on
the machine; no pin lets each one find its own kernel. A pin aimed at a genuine
custom layout is reported and left alone. It also lists the installs already
present with their versions, and prints `Version: old -> new` when it finishes.
- **`hkm-config check` no longer pins `HKM_KERNEL_HOME` for a self-locating
layout** — writing one on behalf of whichever install ran it last is how the
shared pin came to exist. It removes one that has become redundant.
- `hkm upgrade --local` obeys the same scope rule (non-root installs to the user
scope, creating it if absent) and installs the launcher into that scope's `bin`
directory rather than always `/usr/bin`.
- The scaffolded `kernel-autoload.php` tries `~/.local/lib/hkm-kernel` before
`/opt/hkm-kernel`, so a project run under PHP-FPM or systemd resolves the
kernel its owner actually manages. The pre-1.4 user path is still tried.

### Fixed
- **One install silently ran the other's kernel.** `~/.config/hkm/config.env` is
read by every `hkm` on the machine, and `HKM_KERNEL_HOME` was checked before
self-location — so whichever installer wrote that pin last redirected the other
install too. A `.deb` launcher would report its own version while running a
kernel out of the user's home, and upgrading either scope could not move the
number on screen.
- **`hkm upgrade` could not update a user install on Linux.** It only ever
fetched the `.deb` and shelled out to `sudo apt-get`, despite the user-local
tarball being the documented default since 1.3.1. Because `PATH` usually
resolves `~/.local/bin` before `/usr/bin`, the command reported success and the
very next invocation ran the old launcher unchanged. The user scope now
installs from the tarball via its own `install.sh`, with no `sudo` anywhere in
that path.
- **Upgrade decisions used the wrong version.** `hkm upgrade` compared the
LAUNCHER's compile-time stamp against the latest release tag, then went on to
replace a KERNEL somewhere else — two numbers that differ exactly when the
launcher on `PATH` belongs to the other scope. Versions are now read from the
kernel being replaced, and the command names the other scope when it is also
behind instead of reporting an unqualified "you are on the latest version".
- **A `--local` install could never report what it was.** It copied the
checkout's `composer.json`, which carries no `version` field by design, so
`hkm version` read "unstamped" forever and the next upgrade had nothing to
compare. The `git describe` version is now recorded as semver build metadata
(`1.3.1-2-g34abb2c` → `1.3.1+2.g34abb2c`), which Composer accepts and which
semver excludes from precedence — a change of spelling, not of meaning. A
release build still stamps the exact tag or nothing.
- A `--system` upgrade run without root now says so once, up front, with the
command that works, instead of failing one permission error at a time. The
system path no longer prefixes `sudo` unconditionally, which broke on the
containers and CI images where a system install is most useful and `sudo` is
frequently absent.

## [1.3.1] - 2026-08-12

Supersedes 1.3.0, which was tagged from a commit that never reached `master`
(the branch had advanced remotely between the build and the push). Tags are
immutable in this repository, so 1.3.0 was left in place rather than moved —
it builds, but it predates the `php-io-cli` pin below. **Use 1.3.1.**

### Added
- **Domain lists.** `domain` / `subdomain` now take either a string or a LIST,
at all three levels — module-wide (`routeDomain` / `routeSubdomain`), group,
and route. A project serving several hosts can pin a group to "these three and
not that one" instead of duplicating the group per host. The domain is still
part of the route KEY, and a route grouped under a host the project does not
serve is still rejected at boot.
- **Plugin env seeding.** Enabling a plugin writes the environment it declares
in `module.json` `config[]` straight into `.env`, in three shapes: a documented
default is written ACTIVE, a required key with no default is written active but
EMPTY (so the boot failure points at a line you can see), and an optional key
with no default is written COMMENTED. Previously that list was discoverable
only from a boot stack trace, one variable per attempt.
- **A user-local install that needs no root.** Linux releases now ship a portable
tarball alongside the `.deb`; `tools/install.sh` unpacks kernel and launcher
entirely inside `$HOME` and writes nothing outside it. Published with the
release assets, so `curl … | sh` works without a checkout. The `.deb` remains
for multi-user machines and CI images.
- **Scaffold support for `@pageflow/admin`** (Pageflow v1.1.0): a three-state
theme provider (`{ theme, resolvedTheme, setTheme, toggle }` with a "system"
default that keeps following the OS), the sidebar CSS variables the shell
consumes, and a globbed `ui/admin/nav.ts` navigation registry. Both scaffold
surfaces now wrap their tree in `AppErrorBoundary`.

### Fixed
- **`modules/php-io-cli` pinned back to its last loadable commit.** The newer
pointer merged two parallel implementations of unknown-option handling and kept
both, declaring `AbstractCommand::$unknownOptions` twice — a fatal at class
load, so every command built on `AbstractCommand` died, not just the test that
surfaced it. Only the pointer is reverted; which implementation is canonical is
php-io-cli's call.

### Changed
- `hkm doctor` reports which install is actually in use, and whether a stale
`HKM_KERNEL_HOME` pin in `~/.config/hkm/config.env` is overriding it — the
failure that otherwise presents as "my changes do nothing".

## [1.2.0] - 2026-08-12

### Added
- **Route groups.** `groups[]` in `module.json` / `proj.json` states a `prefix`,
`filters`, `requires`, `name` prefix and `domain` once for every route inside;
groups nest (max depth 16). Module-wide `routePrefix` / `routeFilters` /
`routeRequires` / `routeName` / `routeDomain` / `routeSubdomain` do the same for
a whole file. Expanded at BOOT into ordinary flat routes — zero request-time cost.
- **Domain grouping.** A route may declare the host it answers on
(`"domain": "africavoting.local"`, `"domain": "*.example.com"`, or a bare
`"subdomain": "api"`). The domain is part of the route KEY, so one project can
answer `GET /` differently per host. Ungrouped routes stay global; a bare
subdomain answers on that label of every domain. A declared host is validated
against `proj.json` `"domains"`.
- **Parameter types `path` and `enum(a|b)`, and optional `{id?}`.** `path` is a
traversal-safe catch-all (`any` is unchanged and still has no guard);
`enum` members are `preg_quote`d, so no regex can be injected from JSON.
- `HEAD` requests are served by the `GET` route (`ROUTE_HEAD_FALLBACK`), with the
body stripped. Opt-in `405 Method Not Allowed` + `Allow`
(`ROUTE_METHOD_NOT_ALLOWED`) and trailing-slash policy (`ROUTE_TRAILING_SLASH`).
- **`BOOT_CACHE`** — `Kernel::build()` skips recompiling manifests that are already
current. Under PHP-FPM the boot pipeline previously ran on *every request*
(~2 ms, ~150 KB of writes for ~130 routes); with the cache that becomes ~0.02 ms.
Off by default; clear `var/cache/manifests/` on deploy.
- `route()`, `signed_route()` and `url()` global helpers; `UrlGenerator` bound in
the `CoreContainer`. Absolute URLs follow the route's own domain group.
- `signed` route filter (SecurityFilters) — enforces a `signed_route()` link
declaratively, the URL counterpart to `hmac`.
- Two derived manifests beside `route-manifest.php`: `route-index.php` (the
matcher-ready index) and `route-names.php` (the name index `UrlGenerator` reads).
Both optional at runtime — every consumer falls back to the flat manifest.

### Fixed
- **Captured route parameters are percent-decoded and re-validated against their
type.** `/files/..%2F..%2Fetc%2Fpasswd` no longer satisfies `{name}`, and
`/users/Jos%C3%A9` now reaches the controller as `José` rather than `Jos%C3%A9`.
- Route patterns are anchored with the `D` modifier — a trailing newline in the
request path no longer satisfies `$`.
- Literal path text is `preg_quote`d, so `/feed.xml/{id}` no longer matches
`/feedXxml/1`.
- Signed-URL verification compares the query byte-for-byte instead of round-tripping
it through `parse_str()`, which rewrote `.`, ` ` and `[` in parameter names and
made some legitimately signed URLs impossible to verify.
- `UrlGenerator` supports a repeated placeholder (`/a/{id}/b/{id}`), which
previously reported the second occurrence as a missing parameter.
- `resolveEssentialModules()` no longer re-reads every `module.json` a second time
during `build()`.

### Changed
- Route filter stages are resolved once per worker instead of being reconstructed
on every request; filter specs, the handler split and the dependency-graph key
are precompiled into the manifest.
- Dynamic routes are bucketed by their first literal path segment, so a request
tests only the patterns that could match its prefix.
- These now FAIL THE BOOT instead of compiling into a route that silently never
matched: a path not starting with `/`, a duplicated or PCRE-invalid capture name,
a handler without exactly one `@`, a filter alias no `Provider::boot()`
registered, and a route domain absent from `proj.json` `"domains"`.
- `RouteCatalog::publicPaths()` takes an optional `$domain` — the default is
unchanged (shared routes only).

### Docs
- `docs/Sentinel-Routing-Guide.pdf` — a practical, example-driven routing manual.

## [1.1.0-beta.1] - 2026-08-07

First **installable** pre-release of the 1.1.0 line. `1.1.0-dev.2` and
`1.1.0-dev.3` are withdrawn — see below.

### Fixed
- **`composer install` aborted on every machine that took `1.1.0-dev.2` or
`-dev.3`.** The build stamps its version into `composer.json`, and
`1.1.0-dev.N` is not a valid Composer version: Composer's `dev` suffix takes
no counter. `composer install` refuses to run at all on an unparseable
version, so the package unpacked and then failed to resolve its dependencies.
The stamper now validates and skips rather than writing something Composer
rejects, and this release is named `-beta.1`, which Composer accepts — so the
version marker the native distribution needs is actually present again.
- The stamper trimmed `v` from both ends of the version, so any version ending
in `v` lost it — `1.1.0-dev` became `1.1.0-de`, the one pre-release form
Composer does accept.

### Note on upgrading from 1.0.21
A 1.0.21 client has no pre-release filter: it strips the suffix, sees
`1.1.0 > 1.0.21` and offers this automatically. That filter ships **in** this
release, so the behaviour self-corrects after one upgrade. If you took
`1.1.0-dev.2` or `-dev.3` and the install reported a composer schema error,
upgrading to this release repairs it.

## [1.1.0-dev.3] - 2026-08-07

Re-cut of `1.1.0-dev.2` from `main` rather than `master`, so the artefacts
include the PHPStan work that landed with #107. Contents are otherwise
identical — see `[1.1.0-dev.2]` below for the full list.

### Fixed
- **`ProcessLocalLock` could not write its own lock table.** The registry was
typed as an anonymous `object{locks: ...}` shape, whose properties PHPStan
treats as read-only, so every write was an error against a type that
described the shape but never named the one class satisfying it.
- PHPStan is green again: the project scaffolding that binds to plugin
contracts is scoped out of analysis here, since those plugins are
deliberately not dependencies of the kernel. It is analysed in a project that
has installed them.

## [1.1.0-dev.2] - 2026-08-07

Development pre-release. Published so the new tooling can be exercised against
Expand Down
Loading
Loading