mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-18 23:16:38 +03:00
Full lastfm-model rebuild of the ghost skill against current docs.ghost.org research: - Fix JWT signer correctness: hex-decode the secret half before HMAC-SHA256 signing (official contract; literal-hex signing produced invalid tokens), document HS256 + kid header + aud /admin/ + 5-minute token window, add admin_api_audience() derivation and Ghost-scheme error handling with researched signatures (409 UPDATE_COLLISION, 404 non-public guidance, INVALID_AUTH_HEADER hint, 204 delete tolerance). - Extend CLI surface: get-post, update-post (updated_at collision guard), delete-post, create-page, create-tag, posts pagination (--page/--order, meta.pagination surfaced), scheduled posting with --published-at guard; dry-run now previews method/URL/payload exactly as executed. - Add 5 cited reference files (auth/basics, content-vs-admin split incl. draft-visibility asymmetry, endpoint guide, worked recipes, gotchas). - Add scripts/test_ghost.py: 28 offline tests incl. fixed-vector JWT known-answer checks and jq-executed pipeline-consumability chains. - Add evals/evals.json (6 cases incl. npm ghost-cli negative probe). - Rewrite SKILL.md (155-line body) and README in lastfm model. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
54 lines
3.9 KiB
Markdown
54 lines
3.9 KiB
Markdown
# Content API vs Admin API: Which Plane, Which Key
|
|
|
|
Ghost exposes two REST APIs with different credentials, scopes, and content visibility. Picking the wrong one produces the classic failure: everything looks configured, yet drafts are nowhere to be found and nothing errors.
|
|
|
|
## The split at a glance
|
|
|
|
| Aspect | Content API | Admin API |
|
|
| --- | --- | --- |
|
|
| Base path | `/ghost/api/content/` | `/ghost/api/admin/` |
|
|
| Credential | Content key as `?key=<RECORD_KEY>` query param | JWT in `Authorization: Ghost <token>` |
|
|
| Verbs | GET only (Browse, Read) | Full REST per resource |
|
|
| Scope | Published posts/pages/tags/authors/tiers/settings | Everything public **plus drafts, scheduled posts, members, webhooks, images, themes** |
|
|
| Key safety | Safe for browsers and clients (public data only) | Server-side only; signs mutations |
|
|
| Cacheability | Designed to be cached/CDN-fronted | Mutating; publish busts front-end caches |
|
|
| Typical consumers | Site themes, headless frontends, mobile apps | Editorial automation, migrations, scheduling bots |
|
|
|
|
Both key types come from the same Custom Integration screen; an integration has a Content API key and an Admin API key side by side. They are not interchangeable.
|
|
|
|
## Draft-visibility asymmetry (the trap)
|
|
|
|
The Content API **delivers published content only**. Its docs state the key "only ever provide[s] access to public data," and Ghost enforces this at the model layer: public-context post queries carry a non-overridable `status:published` filter.
|
|
|
|
Consequences worth internalizing:
|
|
|
|
1. **Drafts are unreachable via Content API regardless of key validity.** A valid key does not make drafts visible; the request simply never matches them.
|
|
2. **It fails silent, not loud.** Browsing with a valid Content key returns HTTP 200 with only published posts — an empty or partial list, no error, no hint. There is no 403 saying "you can't see drafts."
|
|
3. **Filtering does not bypass it.** `filter=status:draft` against the Content API returns the same published collection; the disallowed filter is ignored rather than rejected.
|
|
4. **Direct reads of non-public posts 404.** Reading `/content/posts/<draft-id>/` behaves as if the post does not exist — consistent with the documented 404 category "data which is not public."
|
|
|
|
The bundled CLI is Admin-API-first precisely because of this asymmetry: `ghost posts --status draft` works only because it authenticates with the Admin JWT, which sees drafts, scheduled, and published posts alike.
|
|
|
|
### Diagnostic checklist when "posts are missing"
|
|
|
|
- Authenticated with the **Content** key? Switch to the Admin key workflow (`GHOST_ADMIN_KEY`); drafts will appear.
|
|
- Using Admin and still missing them? Check `filter=status:` values (`draft`, `scheduled`, `published`) and page through with `--page`.
|
|
- Post visible in Admin UI but 404s from your site code? That is the same asymmetry in reverse: unpublished content never appears on the public plane.
|
|
|
|
## When to use which
|
|
|
|
Use the **Content API** for anything that renders your site to the world: headless frontend builds, static-site generators, search indexes, feeds, mobile apps. It is read-only, key-as-query-param, and cache-friendly.
|
|
|
|
Use the **Admin API** for anything that changes content or needs non-public data: creating and editing posts, publishing/scheduling, managing tags and pages, uploading images, working with drafts before they go live. Keep Admin keys out of browsers, client bundles, and CI logs.
|
|
|
|
A frequent pattern pairs both: editorial automation writes through Admin; the public site reads through Content plus CDN. If a workflow only ever reads published content, prefer Content — smaller blast radius, browser-safe key.
|
|
|
|
## Sources
|
|
|
|
- https://docs.ghost.org/content-api
|
|
- https://docs.ghost.org/content-api/#key
|
|
- https://docs.ghost.org/admin-api
|
|
- https://docs.ghost.org/admin-api/#choosing-an-authentication-method
|
|
- https://docs.ghost.org/content-api/errors
|
|
- https://github.com/TryGhost/Ghost/blob/main/ghost/core/core/server/models/post.js
|