How skilld.dev is shaped. The vocabulary these names use is GLOSSARY.md;
this file says where each concept lives and which layer owns it.
Use the names exactly. Drift breeds shallow modules.
/gh/[owner]is always a GitHub-proxied entity (the repository owner namespace)./@<github-login>is always an authored entity in skilld's own data (the collection-author namespace).- A GitHub org and a collection author can share a name; the
@prefix disambiguates the author namespace.
- registry:
/gh/*andapi/orgs|repos|skills|*. Owns GitHub proxying. - identity (Phase 2): GitHub OAuth, sessions,
userstable, subscriptions, digests. - app:
/,/community,/@<login>/*,/collections/new,api/community|collections|feed|*. Owns native data. - marketing:
/skills,/skills/*,/frameworks/*,/learn/*,/compare/*. Comark renders its Markdown. Owns SEO content. Comparison requirements cover editorial evidence and admission. - admin: existing.
- artifact delivery:
/api/v1/resolutions|artifacts|trusted-root|github/connections, and the internal/api/run-checks/flagsand/api/behavior-readings. Owns exact source resolution, checks, signing, grants, transient delivery, the run checks behind the run check flag, and the behavior readings (ADR-0016).
Cross-layer reads go via HTTP ($fetch('/api/...')), never shared server utilities. Each layer is deletion-testable.
- Platform: request-scoped object on
event.context.platformcarrying every infrastructure binding a handler needs:db(D1),ai(Workers AI),github(resolved client),requestId. Mounted byserver/plugins/platform.ts. Handlers must read bindings from here, never directly fromevent.context.cloudflare.env.dbis one D1 session per request. Safe methods use an incoming bookmark orfirst-unconstrained; mutating methods start withfirst-primary. A write's bookmark rides ad1-bookmarkcookie so the next request reads it. With replication disabled, all session queries use the primary. Keep replication disabled forskilld-db, following the D1 reliability runbook. Cron tasks and queue consumers use the raw binding, which always reaches the primary. - defineApiHandler: the shape of every internal API route:
{ schema, policy, handler, presenter }. Schema is a zod input, policy is an array of atomic predicates AND-ed, handler receives{ body, platform, user, event }, presenter shapes the response. Defined inshared/server/handler.ts. - Policy: a
(ctx) => boolean | Promise<boolean>atom inlayers/<layer>/server/policies/. Composes by array. - Presenter: a
(row) => dtoinlayers/<layer>/server/presenters/. Response shape lives here, never inline in handlers. - Schema: a zod input schema in
layers/<layer>/server/schemas/. Auto-validated bydefineApiHandler.
/api/v1 is the only API surface with a promise: an answer may gain a field and never loses one.
Internal routes may change on any deploy. One descriptor per operation, in
skilld-dev/skilld at packages/sdk/src/contract, drives four things.
This site consumes an exact npm version of skilld-sdk, following ADR-0007:
descriptor (skilld-dev/skilld: packages/sdk/src/contract)
├─ OpenAPI document generated/openapi.v1.json, served at /api/v1/openapi.json, drift test
├─ route checks defineApiOperation in shared/server/operation.ts
├─ SDK createSkilldClient, published as skilld-sdk
└─ parity test test/unit/api-v1-parity.test.ts: one route file per operation
- Operation: one
namespace.verbentry in the contract, such asskills.get. Its ID never changes. Public handlers never receive caller identity. Rate-limit middleware verifies credentials only to select an allowance. Account operations reuse that identity. V1 responses bypass Workers Cache so every network request reaches the limiter. See ADR-0008. Resolution polls draw from their own allowance: ADR-0012. - defineApiOperation: the shape of a
/api/v1route:{ operation, handler }. The descriptor parses the input, checks the credential, and sets the status and cache headers. The handler only loads data. The answer passes the strict producer schema, so an unnamed field fails closed with a 500. - Route file: lives in the layer that owns the data, at the operation's own path:
GET /api/v1/skills/{owner}/{repository}/{name}islayers/registry/server/api/v1/skills/[owner]/[repository]/[name].get.ts. It calls that layer's utilities, or reads the internal route in process. It never duplicates SQL. - v1 presenter: a pure function in
layers/<layer>/server/presenters/<thing>-v1.ts, typedOperationResult<typeof op>. - Failures:
operationFailure(code, detail)returns an expected failure as a value. Every failure answers RFC 9457application/problem+jsonwith six fields, because the skilld CLI rejects any other. - Frozen: the
skills.searchanswer never gains a field while skilld 3.2.0 is in use. - Outside the contract: Artifact delivery keeps its own schemas until it folds in.
Artifact grants accept x-skilld-resolution-id to select the exact Resolution attestation.
One content hash can have several attestations with different sources or signing times.
The grant must return the requested Resolution's attestation, or deny delivery.
Public grants without the header select the newest attestation. Private grants retain Account access checks.
A public build streams the Repository archive from codeload and never holds the whole Skill. The limits, the constraint behind each, and linked files: ADR-0013. A build follows each symbolic link in the Skill folder and packs the files it names: ADR-0014.
After a contract change in the skilld-dev/skilld repository, run pnpm --filter skilld-sdk generate and commit the document.
Publish the new SDK version from that repository's release.yml. Then update this site's exact catalog pin.
The route parity test checks the site against the published contract.
See ADR-0007.
- Service: an object owning an API client or app-wide reactive state. Constructed by
createAppServices(config)inapp/services/, exposed vianuxtApp.$services. Composables that just bind keys or call$fetchare not services. - Public pages render signed out: the server never reads the session for a public page. The server render must not branch on User-Agent either, because
Varydoes not name it. Only then can one stored copy serve every visitor. A page that callsuseBotDetection()on the server needs that call removed before it gets an edge cache rule. The browser loads the session after hydration.useAuth().stateispending,anonymous, orsigned-in, and an auth-dependent control renders a same-size placeholder while it ispending. A page that needs the session on the server uses thesessionorauthroute middleware, which also marks the responseprivate, no-store. - Session reads: server code reads the session with
readUserSession(). It opens a session only when the request carries one, because h3 sets a new session cookie on every other read. ESLint bans a directgetUserSession(). - Markdown negotiation:
shared/content-negotiation.tsdecides HTML or a 307 to the.mdURL fromAcceptandSec-Fetch-Destonly, and every page response names both inVary. - HTML validators:
shared/html-validators.tsgives a page under anedgeCacherule a weak ETag: the build id plus a hash of the HTML. A request that sends it back gets an empty 304. A request with a cookie or anAuthorizationheader gets no ETag. No page sendsLast-Modified, because no one date bounds what a page renders.
| Concept | Canonical |
|---|---|
| Owner hub | /gh/[owner] |
| Repository | /gh/[owner]/[repo] |
| Skill | /gh/[owner]/[repo]/[name] |
| Author | /@<github-login> |
| Collection | /@<github-login>/<slug> |
| Marketing index | /skills |
| Framework page | /frameworks/[name] |
| Comparison | /compare/<slug> |
A Repository that GitHub renamed or transferred moves to its new name, and every /gh URL under the old name answers 301 to the same path under the new one (ADR-0015).
The legacy /people/[handle] and /people/[handle]/collections/[slug] URLs are 410 Gone (with a 301 special-case for /people/harlanzw.com → /@harlanzw).