Koder hub coverage
Koder Hub — Publication Coverage Registry
Tracks which Koder Stack components are reachable via the Hub publication API GET https://hub.koder.dev/api/v1/apps/<slug>. Source of registered components: registries/component-names.md.
The audit probes three name variants per row of the source registry (type ∈ {product, service, engine} — meta and umbrella types are non-publishable and excluded):
<slug>— the canonical registry slug<aliases[0]>— the first coloquial alias, if anykoder-<aliases[0]>— the registry-noncompliant compound form
A row passes when (1) returns HTTP 200. The other variants are auxiliary: a 200 on (2) or (3) without (1) means the package was published under a noncanonical name and should be republished under <slug> (the registry is authoritative; release engineering should align with it).
Status values
| Value | Meaning |
|---|---|
| *K_CANONICAL* | Published as <slug> (registry-aligned) |
| *RIFT_AS:name* | Published only as <name>, where <name> ≠ <slug> |
| *UAL* | Both <slug> and a non-canonical variant return 200 |
| *ISSING* | None of the probed variants exist (expected for components that have not shipped a first release) |
| *RR* | Probe error (rate-limit retry exhausted or network) |
Last audit: 20260520 (Kruze + Box spot probes)
Spot probe 2026
0520:GET https://hub.koder.dev/api/v1/apps/koder-kruze→ HTTP 200,slug=koder-kruze, version1.0.20(Flow has v1.0.23). Aliasedkruze→ HTTP 404. Classified *K_CANONICAL* row added under that section. Hub-side version drift tracked inproducts/horizontal/kruze/app/backlog/pending/157. Kruze had been absent fromcomponent-names.mduntil this commit landed the row.Spot probe 2026
0520:GET https://hub.koder.dev/api/v1/apps/koder-box→ HTTP 404;kboxalias → HTTP 404. Classified MISSING; row added below. Follow-up to publish tracked ininfra/net/box/backlog/pending/107.
Previous audit: 20260514 (post #119 data fix)
Probed via curl -sS https://hub.koder.dev/api/v1/apps/<name> with 0.3s between requests + exponential backoff on 429.
Total publishable components (product+service+engine from component-names.md): *15*
| State | Count |
|---|---|
| OK_CANONICAL | 25 |
| DRIFT_AS | 0 |
| DUAL | 0 |
| MISSING | 89 |
#119 data fix — completed 20260514
All 5 non-aligned publications resolved. The 4 DRIFT cases were renamed via PATCH /api/v1/publish/<old-slug>/rename (#107 primitive: atomically updates apps.slug to canonical AND records old_slug in slug_reservations AND prepends to previous_slugs). Old URLs continue resolving to the canonical record by app_id — verified postrename: `GET apps<oldslug> and
GET appscanonical-slug return the same id. The DUAL case
(kmail + koder-mail) was deduplicated by archiving the kmail
record (soft delete; hard delete is gated by
KODERHUBDEV_DESTRUCTIVE=1 and not enabled in production —
acceptable since kmail` v1.0.0 was a placeholder seed with no real downloads).
| Registry slug | Old slug | App ID | Action | Pre-state | Post-state |
|---|---|---|---|---|---|
koder-term |
kterm |
a4c343cd1830fa2e24724c997eb36112 |
rename | kterm 200 / koder-term 404 |
both 200, same id |
koder-icon |
kicon |
a181b8f67ddcab31e9fb0690e7649c60 |
rename | kicon 200 / koder-icon 404 |
both 200, same id |
koder-pkg |
koder-kpkg |
4dc5ee97514ee0207a95597f5c011a33 |
rename | koder-kpkg 200 / koder-pkg 404 |
both 200, same id |
koder-termux |
koder-ktermux |
a118be2aaa6f80a58c933f1d3f5b2a67 |
rename | koder-ktermux 200 / koder-termux 404 |
both 200, same id |
koder-mail |
kmail (id 0583031114687b1746a1e1eac7ae48c0) |
cdfa21dddf5546d0bc9b31ce31260c16 |
archive kmail |
both 200 (different ids) | kmail 404, koder-mail 200 |
Bonus alias-resolution behavior
After rename, GET /api/v1/apps/<old-slug> returns HTTP 200 with the canonical record in the body (not 301 as the handler intends via ResolveSlug → http.Redirect path). Cause: kdbnext keeps the stale idxAppBySlug secondary index entry pointing the old slug at the app row, so GetAppBySlug(old) still succeeds and ResolveSlug returns redirected=false. This is friendlier than 301 for clients (no extra round-trip) but indicates the index update should be investigated as a follow-up — see products/dev/hub/backlog/pending/ for the index-cleanup ticket if/when one is opened. *unctionally*the outcome matches the #119 acceptance criterion: alias and canonical resolve to the same id.
OK_CANONICAL (27)
| Slug | Alias | Hub version | |
|---|---|---|---|
koder-flow |
kflow |
v1.0.0 | |
koder-hub |
khub |
v2.29.87 | |
koder-eye |
keye |
v1.0.0 | |
kode |
— | v1.7.9 | |
koder-tools |
ktools |
v0.3.2 | |
koder-sky |
ksky |
v1.0.2 | |
koder-domains |
kdomains |
v1.0.0 | |
koder-talk |
ktalk |
v1.0.1 | |
koder-chat |
kchat |
v0.1.4 | |
koder-drive |
kdrive |
v1.20.0 | |
koder-calc |
kcalc |
v1.0.2 | |
koder-beats |
kbeats |
v1.0.0 | |
koder-craft |
kcraft |
v1.0.0 | |
koder-dek |
— | v1.9.30 | |
koder-flash |
kflash |
v1.0.0 | |
koder-sign |
ksign |
v1.0.2 | |
koder-id |
kid |
v1.0.0 | |
koder-pass |
kpass |
v1.0.6 | |
koder-bot |
kbot |
v1.1.2 | |
koder-cloud |
kcloud |
v1.0.0 | |
koder-dash |
kdash |
v1.0.0 | |
koder-term |
kterm |
v1.32.1 | (renamed from kterm 2026 |
koder-icon |
kicon |
v0.2.9 | (renamed from kicon 2026 |
koder-pkg |
kpkg |
v1.0.0 | (renamed from koder-kpkg 2026 |
koder-termux |
ktermux |
v0.118.3-koder1 | (renamed from koder-ktermux 2026 |
koder-mail |
kmail |
v1.0.0 | (DUAL deduplicated 2026kmail archived) |
koder-kruze |
kruze |
v1.0.20 | (added 2026, blocked on Hub admin endpoint per productsdevhub#116#117#103. Aliased probe kruze → 404 as expected; registry-canonical slug is koder-kruze`) |
MISSING (89)
Most of these are aspirational components — registered in component-names.md for naming reservation but not yet shipped a first release. No action needed until the component has artifacts to publish.
| Slug | Alias |
|---|---|
koder-bridge |
kbridge |
koder-box |
kbox |
koder-dev |
kdev |
koder-notebook |
knotebook |
koder-pages |
kpages |
koder-grid |
kgrid |
koder-ci |
— |
krypt |
— |
koder-cal |
kcal |
koder-board |
kboard |
koder-cine |
kcine |
koder-crew |
kcrew |
koder-dok |
— |
koder-form |
kform |
koder-hand |
khand |
koder-imago |
— |
kall |
— |
kampus |
— |
kanvas |
— |
koru |
— |
koder-relay |
krelay |
…and 69 others (full list regenerable from the probe script below).
Refresh procedure
# Extract (type, slug, alias) from registry
awk -F'|' '
/^\| (product|service|engine|meta|umbrella) \|/ {
type=$2; slug=$5; alias=$7
gsub(/^[ \t]+|[ \t]+$/, "", type); gsub(/^[ \t]+|[ \t]+$/, "", slug); gsub(/^[ \t]+|[ \t]+$/, "", alias)
n=split(alias, parts, ";"); first=parts[1]
gsub(/^[ \t]+|[ \t]+$/, "", first)
print type "\t" slug "\t" first
}
' meta/docs/stack/registries/component-names.md > /tmp/registry-slugs.tsv
# Probe Hub (Python script, rate-limited with 0.3s + backoff on 429)
python3 - <<'PY'
import urllib.request, json, time
from urllib.error import HTTPError, URLError
def probe(s, retries=3):
for a in range(retries):
try:
with urllib.request.urlopen(f"https://hub.koder.dev/api/v1/apps/{s}", timeout=10) as r:
d = json.load(r); return 200, d.get('version','?')
except HTTPError as e:
if e.code == 429 and a < retries-1: time.sleep(2*(a+1)); continue
return e.code, '-'
except (URLError, Exception) as e: return -1, str(e)[:30]
with open('/tmp/registry-slugs.tsv') as f:
for line in f:
t, s, al = (line.rstrip().split('\t') + ['',''])[:3]
if t not in ('product','service','engine'): continue
sc, sv = probe(s); time.sleep(0.3)
ac, av = probe(al) if al else ('', ''); time.sleep(0.3) if al else None
ka = f"koder-{al}" if al and not al.startswith("koder-") else al
kc, kv = (probe(ka) if (al and ka != s) else ('', ''))
print(f"{t}\t{s}\t{al}\t{sc}({sv})\t{ac}({av})\t{kc}({kv})")
PYRelated
registries/component-names.md— source of truth for slugs (authoritative)specs/naming/forms.kmd— naming rules (R1R6, T1T11)specs/binaries-and-cli/naming.kmd— binary name derivation (aliases[0] || slug)
Audit cadence
- Refresh on every Hub publication/unpublication.
- Drift cases block release engineering for affected components until
the publication is re
aligned with `componentnames.md`. - MISSING rows are informational only; not a block.
Companion CI workflow
.gitea/workflows/audit-hub-coverage.yml runs the probe automatically:
- *chedule:*daily at 04:17 BRT
- *riggers:*push touching
component-names.md,koder-hub-coverage.md, or the workflow file itself; manual viaworkflow_dispatch - *utput:*state counts in workflow summary + drift/dual table
attached as artifact (
hub-coverage-tsv, 30-day retention) - *ate:*soft-fails if
DRIFT_AS + DUALexceeds baseline (5) + 1,catching new drift without false-flagging the documented state. Tighten to "any drift = fail" once the 5 known cases are resolved.
Manual refresh of this document remains a human task — the workflow exposes regressions but does not auto-edit the prose.