KeyKosh — what has actually been run, and what it produced ========================================================== Last updated 2026-08-24. This page exists because "a wrong-host or expired licence degrades and keeps serving" is the kind of claim a vendor should not be believed on. Below is what was run, against which artifact, and what came back. The test suite itself is not a public repository today. That is a real limit on what you can verify from here: you can check the image, the digest and the signature yourself, but for the assertion results you have my transcript rather than a run of your own. If that is a blocker for you, say so — it moves up the list. 1. THE LICENCE FAILURE PATH — 12 assertions ------------------------------------------- Script: degrade-serves-config.sh Run against: the PUBLISHED image, pulled from ghcr.io — not a local build. Licence used: a real, live-mode purchased PRO key, validated against the production public key BAKED INTO THE IMAGE (fingerprint f0a4401351747025). D-00 database is fresh (no schema from a previous run) D-01 the real key applies against the image's baked production key -> PRO / binding ok / not degraded D-02 seeded org + app + env + 2 properties + an SDK token, at full tier D-03 SDK token read serves config at full tier (the baseline) D-04 a host mismatch is detected, and held at FULL tier by the 1-hour grace window -- a proxy typo self-corrects instead of capping you on day one D-05 past the grace window the bound key degrades to FREE (licensedTier still reads PRO -- the key is not destroyed) D-06 *** THE DEGRADED INSTALL STILL SERVES CONFIG TO AN SDK TOKEN *** HTTP 200, values intact D-07 the encrypted-at-rest secret still decrypts on that degraded read (encryption at rest is not paywalled -- it ships on every tier) D-08 nothing deleted -- org, app and env all still present while degraded D-09 FREE caps are enforced on CREATE only -- a 2nd org is refused 402, existing data untouched D-10 a THIRD host is 421 -- the install answers on its own declared host, not on anything else D-11 correcting the host restores PRO, with no data loss and no re-apply of the key Results: 2026-08-19 11 passed / 0 failed against ghcr.io/k2platform/k2-app:1.0.3 2026-08-21 12 passed / 0 failed against ghcr.io/k2platform/k2-app:1.0.5 The count moved 11 -> 12 because D-00 was added, not because the product gained a behaviour. D-00 asserts the database is genuinely empty before anything else runs, so that no later assertion can pass vacuously against leftover state. 2. THE RELEASE ITSELF — 18 checks --------------------------------- Script: verify-release.sh. It re-derives every row from the registry and from the live site, not from a local build. 2026-08-21 18 passed / 0 failed for tag 1.0.5 cold ANONYMOUS pull (no credentials) digest sha256:10dc99f5372ae71990c038acfb2cc80459042e54c1959fffb1ff63598c5feb4c multi-arch: linux/amd64 and linux/arm64 cosign keyless signature, identity release-k2-app.yml@refs/tags/v1.0.5 the baked licence trust root is the production key, with no dev-key shadow no external host referenced anywhere in the served admin page or stylesheet all 12 self-hosted web fonts served by the image itself the three version pins agree: the lockfile, the compose generator, and the compose file served at keykosh.com/install/docker-compose.yml You can check the first four of those yourself right now, without me: docker buildx imagetools inspect ghcr.io/k2platform/k2-app:1.0.5 cosign verify ghcr.io/k2platform/k2-app:1.0.5 \ --certificate-identity-regexp '.*release-k2-app.yml.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com 3. LICENCE LIFECYCLE -------------------- Integration tests, run against the released 1.0.5 image. 2026-08-21 23 passed / 0 failed / 2 skipped PRO -> PRO+ widens every limit in one step trial -> paid clears the trial window and lifts the limits a lapsed trial key is refused on paste, and the running licence is left alone a trial lapsing under a running install degrades to FREE without bricking, deleting, or ceasing to serve a secret written on one version still decrypts on the next, across the same Postgres and secrets volume domain binding: an apex covers its subdomains, not look-alike domains, and not upward; a foreign Host is 421; a denylisted key drops to FREE The 2 skips are genuine unmet assumptions, not failures: an audit read-window test has no window to walk out of once the stack is licensed for full history. 4. INSTALL, ON A MACHINE THAT HAD NEVER SEEN IT ----------------------------------------------- Script: clean-machine-quickstart.sh. It runs the exact two commands this site gives you -- curl the published compose, then bring it up -- and asserts 23 things about the result. It has no dependency on the source tree by design, because it has to run on a machine that has never had a checkout. 2026-08-21 23 passed / 0 failed Bare Ubuntu 24.04, amd64, Docker Engine 29.7.2, no Docker Desktop. Roughly 80 seconds from a bare VM to a serving install: about 35s of that is installing Docker itself, about 32s is the stack coming up healthy, on a cold 283MB image pull. Your pull time will differ. Among the things it pins: that the amd64 half of the multi-arch manifest is what actually gets pulled; that every published port is bound to loopback and not to all interfaces (the first-run setup page grants ownership to whoever completes it, so publishing it would be the vulnerability); that the bundled Postgres does not collide with a Postgres already on the host; and that the image bakes the production licence key rather than a development one. 5. THE CLIENT SDKS ------------------ One contract, implemented once per language, run against a live server. 2026-08-22 42 passed / 0 failed / 3 skipped (Java, Node, Python) Fifteen scenarios, each implemented once per language against the same written contract. Among what they pin: a token-scoped read returns what the admin API wrote a token scoped to another application is refused the in-memory TTL is honoured, and a failed refresh serves the stale entry an auth failure is NEVER served from the offline file, and says so with the server's container stopped, a valid offline file is served, and an unusable one reports its own error rather than a generic "no file" an admin write reaches a watching client with no polling a BLOCKED change-stream still boots the app, and the client falls back to polling instead of failing an unknown field in the server response never breaks a read The 3 skips are documented asymmetries between the languages, not gaps: two scenarios cover Spring-specific modes that have no Node or Python equivalent, and one covers deprecated environment-variable aliases the Java client deliberately never honoured. The SDKs are MIT licensed and their source is public: github.com/k2platform/k2-sdk-java github.com/k2platform/k2-sdk-node github.com/k2platform/k2-sdk-python 6. ADMIN SSO, AGAINST A REAL IDENTITY PROVIDER ---------------------------------------------- K2's OIDC login is exercised twice. A bundled mock provider covers the protocol matrix, and it is permissive by design -- it accepts any client secret and mints a token for any code. That is what makes the matrix scriptable, and it is also why three of the four things an OIDC client must get right are never actually tested by it. So the same code is run again against a real Okta organisation. 2026-08-24 11 passed / 0 failed / 0 skipped backend, real Okta org 2026-08-24 3 passed / 0 failed / 0 skipped full browser round-trip What the real provider adds over the mock: RS256 id_tokens verified against a third-party JWKS, not a locally minted key a real client_secret_basic token exchange, where a wrong secret is a real 401 authorization codes that are genuinely single-use -- a replay is refused a forged code that fails definitively at the token exchange, not ambiguously the browser round-trip: Okta's own widget, back to /admin logged in, with the token scrubbed out of the URL One finding is worth stating here, because it changes how an install is configured. Okta does not emit the standard email_verified claim -- even though its own discovery document advertises that claim as supported. Microsoft Entra ID behaves the same way. K2 links SSO accounts by verified email, so both providers need OIDC_ASSUME_EMAIL_VERIFIED=true, and there is now a test pinning the claim shape actually observed rather than the one advertised. Discovery metadata is an advertisement, not a promise. Password login keeps working with SSO configured. Enabling an IdP never removes the local login path, which is what stops an unreachable provider from becoming a lockout. Limits, stated so you do not have to ask: - This runs against one organisation on a time-boxed trial, so it is re-run deliberately rather than continuously. - The negative claim matrix -- a user with an unverified email, a user with no email at all -- stays on the mock provider, because each case needs its own directory user. - Entra ID is not in this rig. Its shared behaviour on email_verified is documented, not measured here. - Admin SSO is a Pro+ capability. On any lower tier the login route is not merely hidden, it is absent. 7. ACTUATOR EXPOSURE -------------------- Checked directly against a running 1.0.5 stack on 2026-08-22, because "pull-only metrics, nothing else exposed" is the kind of claim worth curling rather than reading: /actuator/prometheus 200, no credentials, ~40KB of metrics /actuator/health 200 /actuator/info 200 /actuator/env 401 /actuator/beans 401 /actuator/configprops 401 /actuator/loggers 401 Health, info and prometheus are the only three exposed. The metrics endpoint is scrape-only; the running product makes no outbound call of any kind, including for licensing. WHAT IS NOT PROVEN HERE ----------------------- Stated so you do not have to find it out in a comment thread: - No third-party security audit has been done. Disclosure path is at keykosh.com/.well-known/security.txt - The admin UI is an MVP. It covers login, the workspace tree, members, tokens, licence, audit and approvals, and it is not more than that. - Config import is exercised end-to-end for environment-to-environment copies. The other importer formats work but have no black-box test behind them yet. - The production-change approval flow is tested at the unit level, not by a black-box run against a released image the way the licence path is. - Release 1 installs with Docker. Anything else is your own tooling running the same image. Questions, or a claim you want to see run differently: github.com/k2platform/k2support