contentkit

package module
v0.85.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 52 Imported by: 0

README

contentkit

contentkit is the deterministic content library for the Doujins, Hentai0 and marketplace hosts: tenant-scoped content (items, versions, groups), interactions (comments, reactions, favorites, polls), keyword search over that content, the ClickHouse signal plane (consumption, feedback, exposures, popularity, erasure) and the discovery reads over both. It needs no model provider, API key or vector extension. Semantic search belongs entirely to the separate, deferred User Intelligence library; ContentKit has no semantic search configuration or runtime hook.

Design: open-rails-tracker/contentkit/DESIGN.md. Host contract: HOST_INTEGRATION.md. Migrations: docs/migration.md, docs/restore.md.

Release policy

ContentKit is pre-stable and releases on v0.x; its API may change between minor versions. The historical v1.0.0-rc.1 through v1.1.1 tags were premature and are retracted. v1.1.2 is a self-retracted, withdrawal-only marker carrying Go module metadata, not a supported stable API release.

The marker and v0.12.2 identify the same source commit. Go reads retractions from the highest release before applying them, so the marker makes fresh @latest requests select the latest unretracted v0.x release, including future v0.x releases. Existing tags remain immutable; retraction preserves explicit pins and does not automatically downgrade existing consumers. See Go module retractions.

Vocabulary

Name Meaning
tenant_id one site: doujins, hentai0, the marketplace
content_id the host-owned work: a gallery, a video, a listing; a canonical UUIDv7, never reused (Content ids)
content_version_id one selectable version of that work (optional)
group_id a group: tag, artist, series, character, channel, playlist, featured rail
ContentRef {TenantID, ContentKind, ContentID, ContentVersionID *string} — the typed reference every API, key, index and cursor carries

A language is metadata on a document or version, never part of a reference. Every read is scoped to the tenant pinned at construction; a reference of another tenant is an error, never remapped.

Packages

Package Owns
contentref ContentRef, ContentKey, GroupID
contenturl uniform content URLs [/{lang}]/{route}/{CODE}[/{slug}]: permanent 9-character codes per tenant, slugs, merges, legacy aliases, canonical redirects, a JSON lookup; the browser half is @openrails/contentkit-ui/urls (Content URLs)
access Actor, the staff permissions (Permission, Permissions()); the key grammar, rules, the Gate, its listing Filter over content_facets, the request memo and the Billing dependency (Access and offers)
offers offers on content, versions and groups, their OpenRails sync job, free group joins and the library
adapters/openrails access.Billing over the OpenRails client (its own module)
media the registry (Config, kinds, upload paths, private and public presets), ordered manifests with provenance and conditional-write edits, the Store port, direct uploads and commit ops with their HTTP API, reads and HLS playlists, the optional UploadLimiter, and the sweep, deletion, Expose and relays as River jobs
media/s3 Store over aws-sdk-go-v2 (Ceph RGW in production, MinIO in tests), bucket policy and point-in-time Restore
media/image libvips (CGO) producer: Image presets, public presets, zips, editor views, PublishDefaults
media/token media access tokens, shared by hosts and the media gateway
media/layout object keys and the media gateway's host and default rules, dependency-free
media/gateway the media gateway's handler (cmd/media-gateway)
media/video ffmpeg producers: byte-range fMP4 HLS ladders with audio, subtitle and sprite tracks, MP4 per rung, audio, subtitles, frame grabs; Frames for the frame picker
media/worker the media worker: one process for every producer, built from the host's registry (cmd/media-worker is the stock build)
media/workqueue the host's side of the worker: its per-host River schema, insert-only Queue (enqueue, cancel), encode progress
catalog content items of the configured kinds (a blog is kind blog): per-language text, creators, versions, segments, draft/published/deleted with scheduling, listing, access rules, restrictions, attributes, covers and inline images; content_facets, the one visibility rule (Viewer) and ContentKit's own content resolver, the content_events outbox, search documents, Totals, its jobs and /v1/contents routes
content comments (in language threads), reactions, favorites, polls (multiple-choice and free-text) and their counts on content items, versions and groups; banned words and IP bans; authors' names from userinfo.Lookup and avatars from media, the ContentProcessor port, poll images through Media, the optional ContentModerator (held/review queue, shared with the catalog) and AnswerClassifier ports, per-user interaction limits, owner and global comment bans, and the HTTP routes
search PGroonga keyword search (exact/alias/prefix/typo, EN/ZH/JA/KO) with a visibility join, documents and dirty queue, RRF
groups groups of content by kind (kinds.Config.GroupKinds): localized names/aliases and text, attributes, covers, parents, scopes, edges, merge; content- and version-level memberships with roles, positions and windows; effective groups for facets, per-language counts, typeahead documents, /v1 routes
signal ClickHouse signal plane: canonical signals, compact subject state, daily rollups, windows, erasure fence, exposures/attribution, repair
popularity named ranking policy (PolicyV1) over the window metrics: ClickHouse RankExpr and Go Score in agreement, literal windows, session scorer, group popularity through the Catalog port (*groups.Store)
discovery the public reads under /v1: lists, search, typeahead, similar and recommended contents, release dates, content filters, history, signals and exposures, as cards under one visibility rule; the popularity job; the Candidates port, Engagement, Fallback and the shared Recommender policy
eval lexical golden-case evaluation, reports, baselines
migrations PostgreSQL migration chain and ClickHouse baseline
adapters/openrails its own module (opt-in): access.Entitlements over OpenRails CheckEntitlements (one request per read)
e2e its own module: every route end to end over PostgreSQL, MinIO and a real AuthKit
root Engine: New (migrates its own schema), Start (its River jobs), Routes (route groups under /v1), Client (typed in-process client), Ready, Probes, RiverJobs, Close
server its own module: the standalone contentkit binary (server/cmd/contentkit)

Engine

ContentKit runs in the host's process the way OpenRails does: one New, one Start, routes the host mounts.

ck, _ := contentkit.New(ctx, contentkit.Config{
	Tenant:     "doujins",
	Database:   contentkit.DatabaseConfig{Schema: "doujins_contentkit", RiverSchema: "doujins_river"},
	Content:    siteKinds, // kinds.Config: languages, tiers, content and group kinds
	Media:      &contentkit.MediaConfig{Config: registry, Delivery: contentkit.DeliveryConfig{SigningKey: key}},
	ClickHouse: contentkit.ClickHouseConfig{Database: "doujins_signal", Addr: chAddr},
}, contentkit.Deps{Postgres: pool, Auth: ak.Authenticator(), Users: ak.UserInfo(),
	Billing: ckopenrails.Billing(merchant), Store: bucket})
fleet, _ := riverhelpers.New(ctx, pool, &river.Config{Schema: "doujins_river"}, ak.RiverJobs(), ck.RiverJobs())
_ = ck.Start(ctx, contentkit.WithRiverClient(fleet)) // or ck.Start(ctx): its own River
routes, _ := ck.Routes(contentkit.Routes{Prefix: "/api/contentkit", Groups: contentkit.AllRoutes})
for _, r := range routes {
	mux.Handle(r.Path, r.Handler)
}
  • New creates or upgrades ContentKit's tables in Database.Schema (default contentkit), River's and the media worker queue's, and with ClickHouse.Addr the signal plane, each under its migrator's advisory lock. It may share the host's database, never its schema. PGroonga and pg_trgm live in public.
  • Config is plain data (YAML/JSON); Deps holds connections and providers. Media's namespace is the tenant; content kinds' media are their versions' (media.ForContent, the media/versions service). All content is the catalog's: hosts create it through /v1/contents (or Client()).
  • Start runs every background job: search indexing, preference sync, co-engagement, projection repair, erasure, groups' counts and windows, the catalog's scheduled publishing, event delivery, counts and purge, offer sync and the media jobs. Hosts schedule nothing. Start before serving: writes queue jobs.
  • The engine builds the access Gate from Config.Content and Deps.Billing, and the offers service from Config.Offers.
  • Client() is the HTTP surface as a Go client acting as the host, over an in-process transport (for example PublishNotifications, CreateContent).
  • Ready fails until Start; Probes are the optional dependencies (ClickHouse, the bucket, Redis); EraseSubjects is account erasure.

server/ runs the same engine standalone: a YAML config file, secrets from the environment, an AuthKit resource server, OpenRails' remote client and health at /livez, /readyz and /statusz (server/cmd/contentkit).

HTTP API

Routes returns each selected route group as a subtree under {Prefix}/v1. No auth middleware goes in front: each route's gate asks Deps.Auth who the request is (once) and admits it by its tier:

Group Sub-path Needs
ContentRoutes /v1: the content model (GET /v1/kinds), content items and versions (/v1/contents, /v1/manage/contents), catalog totals, comments, reactions, favorites, polls, comment bans, moderation always
UploadRoutes /v1/media/upload Config.Media
MediaRoutes /v1/media: reads and HLS playlists; /v1/contents/{id}/versions/{vid}/…: reader plan and metadata, fork, download Config.Media
CodeRoutes /v1/codes/{code} always
GroupRoutes /v1/groups, /v1/contents/{id}/groups: listings and pages, writes for root:groups:write always
NotificationRoutes /v1/notifications: the inbox, and the host's publish route always
OfferRoutes /v1/contents/{id}/offer, /v1/groups/{id}/offer and join, /v1/me/library, /v1/offers Deps.Billing
Tier Admits
open anyone; never authenticates
public anyone: anonymous without a credential, else the subject it proves (a credential that fails is 401 with the provider's challenge)
user a signed-in person (401 anonymous, 403 an application)
staff a person or an application holding the route's permission, one of access.Permissions() (root:<resource>:<action>), in the Authenticator's root scope, by its Verified.Can (403 without it)

Refusals carry auth.Refuse's status and WWW-Authenticate; an auth outage is 503. ContentKit names its staff permissions (access.Permissions()); the host declares them in its auth's roles from those constants. They are root-wide; per-item rights (creators, owner-group editors) are the catalog's. Behind a proxy, set Request.RemoteAddr to the client's address before the routes: anonymous reactions, votes and limits are keyed by it. No content kind may be named media, codes, groups, contents, kinds, catalog or notifications.

Every route is declared once, in its module's route table (internal/httpapi): method, path, tier, query, bodies and error codes. The table mounts the handlers and generates api/openapi.json, docs/api/routes.md and the browser SDK's sdk/ui/src/client/generated/{routes,wire,error-codes}.ts. After changing a route or a wire type, run go generate ./internal/contract; TestGeneratedContractIsFresh fails while a generated file is stale.

Documents are one per content item or group and language. The engine's search job keeps them current from the dirty queue and a bounded backfill; rejected documents are recorded in content_search_invalid until their source changes. Search, typeahead and discovery are internal (internal/hub) until their routes land (#122). A hit's score is the keyword match tier (exact title 1, alias 0.9, token/prefix 0.75–0.77, typo 0.5–0.52); queries are capped at 256 characters, 16 tokens and two seconds.

Errors

Every module answers failures with one flat body. Branch on code; error is a human message and may change. A code's own members appear only with it (retry_after, action, ban, blobs, details). The codes and their statuses are listed in docs/api/routes.md.

{"error":"not found","code":"not_found"}

5xx bodies carry no cause: it goes to Options.Logger (slog.Default() when unset) with the request method, path, status and duration. Postgres constraint names, driver text and stack traces are logged, never served.

Access and offers (access, offers)

ContentKit gives entitlement keys meaning; OpenRails grants them and never parses one. Keys are constructed, never typed:

Key Opens
access.ContentKey → <tenant>:content:<id> every version of a content item
access.VersionKey → <tenant>:content:<id>/<version> one version
access.GroupKey → <tenant>:group:<id> every member of the group, and content whose rule names it
access.TierKey → premium versions whose rule names the tier (kinds.Config.Tiers)

A Rule is stored as JSON on content (content_items.access), as a version's override (content_versions.access) and on sellable groups: {"level": "public" | "tier" | "group" | "ppv" | "group_ppv", "tiers", "group_id", "preview": {"pages" | "segments"}, "free_after"}. Rule.Validate(kinds) checks it; Rule.Facet() projects it onto content_facets. A version opens with any of: its version key, its content key, the key of a group holding it, a tier its rule names, its rule's group (group), or a free rule (public, past free_after).

A content item's body is part of what it sells: reads return it only to its editors and to viewers who open a version; others get description and the paywall decision. Content, version and card decisions list offers, each sold key's offer (key, target, terms, price_id; a paid one once synced), the shape of a group's access.offer; tier keys are the host's to label. A group_ppv item's product requires the group key (OpenRails required_entitlements), so OpenRails refuses a non-member's order (entitlement_required) for the key the decision requires. Content a group owns (owner_group_id, a channel) lists under owner_group= and carries the group's card as owner; owning is not membership and opens nothing.

gate, _ := access.NewGate(access.GateConfig{Tenant: "onlydemo", Kinds: cfg.Kinds, Billing: ckopenrails.Billing(client)})
f, _ := gate.Filter(ctx, actor)                  // one billing read: tiers, content and group keyspaces
pred, args, _ := f.SQL("f", access.Hide)         // over content_facets f: only what the viewer opens
keep, short, _ := gate.Settle(ctx, actor, f, items) // free unless a keyspace was truncated
ds, _ := gate.Decide(ctx, actor, items)          // Allowed, or Sell, Requires, Preview, FreeAfter
prices, _ := offerSvc.Live(ctx, ds[0].Sell)      // which Sell keys are on sale, with their prices
  • access.WithMemo(ctx) keeps the viewer's Filter and the keys read for the request: a request costs one billing read. Anonymous viewers cost none.
  • Tiers in kinds.Config.TokenClaims are answered from the verified token when its auth.Verified implements access.Entitled; a refund then lags by the token TTL.
  • A failed read fails closed: Filter.Unknown(), Decision.Unknown and an error wrapping access.ErrUnavailable; free rules keep serving.
  • Past access.HeldLimit keys a keyspace is truncated: SQL(Hide) admits its candidates and Settle decides them in one exact read.

access.Billing is the billing dependency: Held, catalog edits (products and prices) and free grants. adapters/openrails.Billing implements it over the OpenRails client, embedded or remote.

offers sells content, versions and groups (PUT|DELETE /contents/{id}/offer, /contents/{id}/versions/{vid}/offer, /groups/{id}/offer). Its River job syncs each target to one OpenRails product whose key and only entitlement is the access key, named after the title, with one price (offers.PriceKey): a reprice is a new price, a withdrawal archives the product and buyers keep their key. A free group offer (amount 0) is joined with POST /groups/{id}/join; GET /me/library lists the content a viewer bought.

Media

Media is an app-defined, self-describing file system in one private bucket; no database table records what media exists. The app declares its kinds in one registry (media.Config); HOST_INTEGRATION "Media" covers the registry, commit ops, reads and exposure.

{namespace}/{kind}/{id}/manifest.json         gzip JSON: the ordered file list with provenance; never served
                       /private/sha256-{hex}  every blob: uploads, derived files, editor views (token)
                       /public/{name}         app-declared names, e.g. cover-460.webp (anyone)
                       /temp/{name}           staged uploads (u-{uuid}) and in-flight writes; never served
{namespace}/{kind}/{id}/{version}/...         a content version's item, laid out alike
{namespace}/{kind}/_default/public/{name}     a public preset's default image

A content kind's items are its versions, {kind, content, version}, each in its content's folder beside the content-level item (its cover); other kinds' items have no versions. Private blobs are content-addressed and immutable; public names carry a generation. Every URL is https://media.<site>/v1/{namespace}/{kind}/{id}[/{version}]/{public|private}/{name}. media/versions publishes versions to viewers (see HOST_INTEGRATION "Media").

Config.Media is the registry (media.Config, without hooks) and how it is served; the engine builds the uploads, reads, journal, worker queue and jobs from it and Deps.Store. Deps.PurgePublic hears of overwritten public URLs for a CDN purge. Every media item is ContentKit's: content kinds' through the catalog's resolver, groups', avatars' and, when the registry declares the poll kind, poll images.

The user-avatar kind comes from github.com/open-rails/contentkit/media/avatar; avatar.User() includes its upload rules and embedded default silhouette. Publish defaults with media/image.PublishDefaults as part of deployment.

Manifests. Every edit runs under the Locker (required: PGLocker, a Postgres advisory lock shared by every process on the bucket) and writes with If-Match (or If-None-Match: *) once the store reports conditional PUT, retrying on conflict. The edit is normalized (canonical order, download names, dropped originals) and validated; an unchanged manifest is not written. Reads go through an in-process cache bounded by bytes and revalidated by ETag, so a read is never stale. A 2,000-page gallery is about 1.5 MB of JSON and 400 KB stored; a 2-hour video about 5 KB, its segment tables living in index blobs. Every read and write stops at MaxManifestBytes (8 MiB of JSON; the default 128 MiB cache holds five). Edits stop 4 KiB short of it and only when they grow the manifest, so a full item can still shrink and the hidden and full flags always fit. A commit is refused (413 too_large) when the item, once processed, would pass that: it projects every output its presets have yet to write. When outputs still overrun it, the worker marks the item full instead of recording them: producers then write no private output for it (its public files still render), and editor reads report full (state full), until a commit frees the bytes the refused record was short of (deficit, counting what removed uploads would still have added). An item holds at most MaxUploads (10,000) uploads, an upload's meta at most 4 KiB of JSON and the item's 16 KiB; names are capped at 200 bytes of JSON, written unescaped (< is one byte). The manifest object records the quota its uploads were charged, so deleting an item never needs it to decode, and hiding deletes public/ before touching it.

The bucket is optional at startup. s3.New never dials; register store.Check(ctx, prefix) as the host's optional S3 dependency probe. Its first success probes the backend's capabilities unless Config.Capabilities declares them; a throttled or cut-off probe records nothing. An unreachable or 5xx bucket is media.ErrUnavailable: 503 unavailable over HTTP, and in media jobs a River snooze (not an attempt) while a fresh Check confirms the outage, capped by MaxOutageSnoozes (media.SnoozeUnavailable).

Uploads go straight to the bucket, to a staged name: up to 64 MiB is one PUT to temp/u-{uuid} signed with its type, length and SHA-256, larger files are multipart (8–16 MiB parts, each signed with its length and SHA-256, resumed through ListParts, completed by the server from a signed ticket; nothing is stored). The browser's hash only finds an identical blob already in the folder (no upload). After the commit, the worker's place job hashes each staged upload and writes it to private/sha256-{hex} of the bytes it read (Manifests.Place); an existing blob is never overwritten, so a blob always holds the bytes of its name. The optional UploadLimiter (media.NewPGLimiter) rate-limits uploaders and charges each upload's size (at least 64 KiB) to the grant's Owner; growth past the quota fails with 413 quota_exceeded, and deleting an item releases it.

Media gateway (cmd/media-gateway, media/gateway, image ghcr.io/open-rails/contentkit-media-gateway:{tag}): public/ to anyone (public, max-age=300, stale-while-revalidate=86400, falling back to the kind's _default for declared names), private/sha256-{hex} with the item's token in ?t= or the mt cookie (private, immutable). A token opens every private file of its item or none; ?dl={name} serves the file as a download under that name when it has the file's type. Everything else and every denial is one no-store 404. It keeps no state: rate limit the media host at the ingress; the read API limits the items a viewer opens per hour (ReaderOptions.Issuance). It needs MEDIA_GATEWAY_S3_ENDPOINT, a key reading only */private/* and */public/*, MEDIA_GATEWAY_TOKEN_KEY and _TOKEN_KEY_PREVIOUS, MEDIA_GATEWAY_HOSTS (media.doujins.ai=doujins,accounts; media.hanime.media=hentai0,accounts), MEDIA_GATEWAY_BUCKETS, each namespace's bucket, one per app (doujins=prod-doujins; hentai0=prod-hentai0; accounts=prod-doujins), MEDIA_GATEWAY_CORS_ORIGINS and MEDIA_GATEWAY_DEFAULTS (layout.FormatDefaults(media.GatewayConfig(reg).Defaults)).

The media worker (media/worker) runs every producer from the host's worker River schema (media/workqueue, MEDIA_WORKER_SCHEMA, per host: hosts sharing a database never share one): images, zips, public presets and editor views (media/image, libvips) and HLS, MP4, audio, subtitles and frames (media/video, ffmpeg). The host presigns, commits, exposes and reads, and links only media/workqueue. The worker hands readiness (Hooks.ItemReady), purges (Hooks.PurgePublic) and sweeps back to the host's River schema (MEDIA_HOST_RIVER_SCHEMA), so the stock build (cmd/media-worker, image ghcr.io/open-rails/contentkit-media-worker) needs only the registry as JSON (MEDIA_KINDS_FILE): an engine's is Config.MediaKinds(), which contentkit media-config -config contentkit.yaml prints. Hosts with a Private.Choose build their own:

cfg, _ := worker.FromEnv(ctx) // DATABASE_URL, MEDIA_S3_*, MEDIA_WORKER_SCHEMA, MEDIA_HOST_RIVER_SCHEMA, MEDIA_WORKER_*
cfg.Kinds = reg                // the host's registry
w, _ := worker.New(ctx, cfg)   // no DDL: the host's migrate step runs workqueue.Migrate
_ = w.Run(ctx)                 // until SIGTERM; running jobs get MEDIA_WORKER_SHUTDOWN_GRACE

It never exits for a missing dependency: Run takes no jobs until the bucket answers, and MEDIA_METRICS_ADDR serves /livez, /readyz, /statusz and app_dependency_up with /metrics.

Images (media/image, CGO over libvips): WebP at each Image spec (inside or cover box, quality, blur), through the upload's edit (crop in EXIF-oriented source pixels, then a clockwise quarter rotation); nothing is upscaled. GIF and WebP animations keep every frame, delay and loop count; MaxPixels (100 MP over all frames), MaxFrames (1000) and MaxAnimationSeconds (60) bound sources, and a stored source over its Upload's MaxBytes is refused unread. Uploads feeding image presets take only media.ImageTypes (JPEG, PNG, WebP, GIF, AVIF, HEIF, TIFF), the declared type binds the decoder, and every other libvips loader is blocked (libvips 8.13 or later). Public presets render every width through the same spec (a width past the edited image at its width) and carry from and fp as object metadata.

Video (media/video): an HLS preset encodes the ladder (rung = short side, default 2160/1080/480, none above the source) in each of the worker's codecs (MEDIA_WORKER_CODECS, default av1,h264), plus AAC per audio track, WebVTT per text subtitle and a seek sprite. Each rendition is one byte-range fMP4 blob whose segment table is its own index blob; stages are published rung by rung (the upload stays pending until the last), a compliant top rung is stream-copied, and sources outside the aspect bounds fail with video_aspect_unsupported. An MP4 preset muxes H.264 at one rung with the default audio; Audio gives an HLS track and an M4A (optional EBU R128 loudness); Subtitles converts SRT and SSA/ASS to clean WebVTT. Playlists are built per request by the read API. Encode progress comes from workqueue.NewProgressSource (ReaderOptions.Progress).

Jobs (media.Jobs, composed into the host's River client): the sweep collects garbage by manifest reference (unreferenced private blobs a grace period old, unexpected public names at once, temp/ by age); folder deletion (with a late-upload second pass and quota release); Expose; Regenerate; SweepOrphans; and the worker's relays.

Paywalls: media gates each item through Hooks.Resolver; one item read is one billing read, and its token covers every file and segment.

Browser package (sdk/ui, @openrails/contentkit-ui on npm, one version per release): the client (uploads, commit ops, reads, waitFor, frames, HLS), content URLs, React hooks and styled components. See sdk/ui/README.md.

Groups

Group kinds come from kinds.Config.GroupKinds (ordered, parent, scoped_by, owner, sellable, auth_scope, windows, version_level, roles, attributes). Writes are batches in one transaction; memberships keep content_facets.group_ids current, Recount (an engine job) rebuilds per-language counts and automatic covers from the facets, and RefreshWindows re-derives facets as rail windows open and close.

The engine builds the store from Config.Content, serves it as GroupRoutes and runs Recount (every 15 minutes) and RefreshWindows (every minute) as its jobs; groups' keyword documents are its search job's.

Catalog

catalog.Store owns content items of the kinds in Config.Content.Kinds. Writes are batches; each transaction keeps content_facets current (rules through access.Rule.Facet, groups through EffectiveGroups), marks search documents dirty, queues content_events, renames offers' products after titles and calls media (expose, republish, fork, delete). Published with a future publish_at is scheduled until a job makes it live. A deleted item stays readable to holders of its own keys; its media is erased after DeletedRetention unless it was sold.

The Viewer is the one visibility rule: live content by its listing (listed; members for key holders; unlisted by direct link), the site's scope and the viewer's content filters, with access.Filter.SQL for lists and Gate.Decide for items; staff (root:content:view-restricted), creators and owner-group editors see everything. Discovery lists and searches under it; media and interactions resolve through it. Held text goes to the shared review queue, screened by Deps.Moderator. GET /v1/catalog/totals (kinds, languages) counts what anyone and each tier opens, per kind and language; a sibling deployment reads it with catalog.NewTotalsClient(baseURL, nil).Totals(ctx, catalog.TotalsQuery{Kinds, Languages}).

Signal plane and discovery

The signal plane runs inside the engine over Config.ClickHouse; its public routes (signals, exposures, history, popular, similar, recommend) land with #122.

  • Identity is (tenant, content ref, subject, type, event id); retries, reordering and revisions converge, nothing is incremented. Record the work view and, separately, the selected version's view (g1.WithVersion(v)).
  • Work reads (History, Popular, SeenIDs, co-engagement) count the work once across versions; States and Metrics read exactly the references given, work or version.
  • Windows are whole UTC days, 7/30/90/365/all, no decay.
  • Rank by a named policy, not the default rank: popularity.ByName("v1"), popularity.New(popularity.Config{Source: hub, Policy: policy}) over the engine's hub, then ranker.Popular / ranker.Scores / ranker.Groups (docs/popularity-policy.md).
  • Similar and recommended content draw candidates from Deps.Candidates (default: co-engagement); see discovery candidates.
  • EraseSubjects is account erasure with a quorum-written fence; see HOST_INTEGRATION.md and, for interaction data, interaction erasure.
  • Reactions and favorites reach the signal plane from their own revisioned rows: the engine's preference job syncs them (watermark with a commit overlap) and re-sends them daily; see HOST_INTEGRATION.md.

Testing

CONTENTKIT_TEST_URL=postgres://...  CONTENTKIT_PROFILE_URL=postgres://... \
CONTENTKIT_TEST_CH_ADDR=localhost:9000 CONTENTKIT_TEST_CH_USER=... CONTENTKIT_TEST_CH_PASSWORD=... CONTENTKIT_TEST_CH_CLUSTER=... \
go test ./... -race -count=1 -p 2

Media tests also need an S3 backend (CONTENTKIT_TEST_S3_ENDPOINT, _ACCESS_KEY, _SECRET_KEY, optional _REGION, _BUCKET and _REQUIRE; see media/internal/s3test). CI runs them on MinIO; media/image runs in its own CI job with libvips, and the other jobs exclude it. To record a Ceph RGW release's capabilities, point the same variables at an RGW bucket and run go test ./media/... -v -count=1; the log prints the probed capabilities. Manifest edits run under PGLocker, so the tests also need CONTENTKIT_TEST_URL (they skip without it). media/video tests also need ffmpeg and ffprobe on PATH (they skip without them unless CONTENTKIT_TEST_FFMPEG=1).

Backend Conditional PUT SHA-256 enforced Notes
MinIO RELEASE.2025-09-07 yes yes drops AbortIncompleteMultipartUpload (expires uploads itself)
Ceph 19.2 / 20.2 standalone dbstore RGW no no wrong Range bytes; not representative of RADOS-backed RGW
production Ceph RGW (RADOS, 2026-09-24) no (If-Match yes, If-None-Match: * ignored) no Range, response-content-disposition, versioning, lifecycle (incl. AbortIncompleteMultipartUpload) and multipart work; hosts wire PGLocker

Tests run against real PGroonga Postgres and ClickHouse+Keeper and skip without the variables; CONTENTKIT_PROFILE_URL needs CREATEDB. Regenerate the eval baseline with CONTENTKIT_EVAL_UPDATE=1.

User inboxes, native comment events and OpenRails imports use the shared notification library. See notifications for APIs, identity mapping, retained-history semantics and erasure.

Documentation

Overview

Package contentkit is ContentKit running in this process: the content library a host configures and mounts, as it does OpenRails and AuthKit.

ck, err := contentkit.New(ctx, cfg, deps)  // migrates its schema
err = ck.Start(ctx)                        // its jobs, on its own River
routes, err := ck.Routes(contentkit.Routes{Prefix: "/api/contentkit", Groups: contentkit.AllRoutes})

The routes, the Go client (Client) and Ready, Probes and Close are the whole surface: hosts never read or write ContentKit's storage.

Index

Constants

View Source
const DefaultSchema = "contentkit"

DefaultSchema is Config.Database.Schema's default.

View Source
const QueueContentKit = "contentkit"

QueueContentKit is the queue RiverJobs contributes. Hosts may set its concurrency in the shared River config; it needs at least one worker.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClickHouseConfig added in v0.83.0

type ClickHouseConfig struct {
	// Database is the signal plane's dedicated database; required with
	// Deps.ClickHouse or Addr.
	Database string `yaml:"database" json:"database"`
	// Addr (native protocol, "clickhouse:9000"), Username and Password let
	// New apply the signal plane's migrations, and open the connection when
	// Deps.ClickHouse is nil. Without Addr, New only checks the schema.
	Addr     string `yaml:"addr" json:"addr"`
	Username string `yaml:"username" json:"username"`
	Password string `yaml:"password" json:"password"`
	// Cluster runs the migrations' DDL ON CLUSTER.
	Cluster string `yaml:"cluster" json:"cluster"`
}

ClickHouseConfig is the signal plane's ClickHouse database.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the typed Go client of the HTTP surface: one method per route (client_gen.go, generated from the route catalog), named by its verb and path (PATCH /v1/groups/{id} is PatchGroup). Engine.Client makes one over the in-process transport, acting as the host; NewClient one for a remote ContentKit. It is safe for concurrent use.

func NewClient

func NewClient(base string, hc *http.Client) *Client

NewClient is a client of the ContentKit served at base, the host's prefix ("https://example.com/api/contentkit"). hc carries the caller's credentials (its Transport sets Authorization); nil is http.DefaultClient, anonymous.

func (*Client) DeleteComment added in v0.83.0

func (c *Client) DeleteComment(ctx context.Context, cid string) error

DeleteComment is DELETE /v1/comments/{cid} (public). Deletes a comment, leaving a tombstone: its author, or a root:comments:moderate holder.

func (*Client) DeleteCommentBan added in v0.83.0

func (c *Client) DeleteCommentBan(ctx context.Context, user string) error

DeleteCommentBan is DELETE /v1/comment-bans/{user} (user). Lifts the caller's ban on a user.

func (*Client) DeleteCommentReaction added in v0.83.0

func (c *Client) DeleteCommentReaction(ctx context.Context, cid string) (content.ReactionCounts, error)

DeleteCommentReaction is DELETE /v1/comments/{cid}/reaction (public). Clears the caller's reaction to a comment.

func (*Client) DeleteContent added in v0.83.0

func (c *Client) DeleteContent(ctx context.Context, id string) error

DeleteContent is DELETE /v1/contents/{id} (public). Deletes a content item: holders of its key still read it; one never sold is erased after the retention. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) DeleteContentFavorite added in v0.83.0

func (c *Client) DeleteContentFavorite(ctx context.Context, id string, q DeleteContentFavoriteQuery) (content.FavoriteState, error)

DeleteContentFavorite is DELETE /v1/contents/{id}/favorite (user). Unfavorites a content item or one of its versions, also one no longer visible. Answers the state with its favorite count.

func (*Client) DeleteContentOffer added in v0.83.0

func (c *Client) DeleteContentOffer(ctx context.Context, id string) (offers.Offer, error)

DeleteContentOffer is DELETE /v1/contents/{id}/offer (editor). Withdraws a content item's offer; buyers keep it. For creators of the content, editors of its owner group (its owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope) and staff holding root:offers:write.

func (*Client) DeleteContentReaction added in v0.83.0

func (c *Client) DeleteContentReaction(ctx context.Context, id string, q DeleteContentReactionQuery) (content.ReactionCounts, error)

DeleteContentReaction is DELETE /v1/contents/{id}/reaction (public). Clears the caller's reaction to a content item or one of its versions.

func (*Client) DeleteContentText added in v0.83.0

func (c *Client) DeleteContentText(ctx context.Context, id string, language string) (catalog.Content, error)

DeleteContentText is DELETE /v1/contents/{id}/text/{language} (public). Removes a content item's text in one language. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) DeleteContentVersion added in v0.83.0

func (c *Client) DeleteContentVersion(ctx context.Context, id string, vid string) error

DeleteContentVersion is DELETE /v1/contents/{id}/versions/{vid} (public). Deletes a version; a content item keeps at least one. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) DeleteContentVersionOffer added in v0.83.0

func (c *Client) DeleteContentVersionOffer(ctx context.Context, id string, vid string) (offers.Offer, error)

DeleteContentVersionOffer is DELETE /v1/contents/{id}/versions/{vid}/offer (editor). Withdraws a version's offer; buyers keep it. For creators of the content, editors of its owner group (its owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope) and staff holding root:offers:write.

func (*Client) DeleteGlobalCommentBan added in v0.83.0

func (c *Client) DeleteGlobalCommentBan(ctx context.Context, user string) error

DeleteGlobalCommentBan is DELETE /v1/global-comment-bans/{user} (staff). Lifts a tenant-wide comment ban.

func (*Client) DeleteGroup added in v0.83.0

func (c *Client) DeleteGroup(ctx context.Context, id string) (groups.Group, error)

DeleteGroup is DELETE /v1/groups/{id} (editor). Marks a group deleted, retires its AuthKit scope and hides its media; a user-owned group's content is deleted with it, and restoring the group (PATCH state active) brings that content back, and its media within the retention. Staff holding root:groups:write write any group; a signed-in person creates groups of user-owned kinds, as their owner and within the kind's max_per_owner, and the owner and holders of {kind}:settings:manage in its AuthKit scope edit names, text, attributes, cover, avatar and state.

func (*Client) DeleteGroupCommentBan added in v0.83.0

func (c *Client) DeleteGroupCommentBan(ctx context.Context, id string, user string) error

DeleteGroupCommentBan is DELETE /v1/groups/{id}/comment-bans/{user} (user). Lifts a user's ban on a user-owned group and its content.

func (*Client) DeleteGroupEdges added in v0.83.0

func (c *Client) DeleteGroupEdges(ctx context.Context, id string, body []groups.EdgeInput) ([]groups.Edge, error)

DeleteGroupEdges is DELETE /v1/groups/{id}/edges (staff). Removes edges from a group.

func (*Client) DeleteGroupFavorite added in v0.83.0

func (c *Client) DeleteGroupFavorite(ctx context.Context, id string) (content.FavoriteState, error)

DeleteGroupFavorite is DELETE /v1/groups/{id}/favorite (user). Unfavorites a group, also one no longer visible. Answers the state with its favorite count.

func (*Client) DeleteGroupJoin added in v0.83.0

func (c *Client) DeleteGroupJoin(ctx context.Context, id string) (offers.GroupJoin, error)

DeleteGroupJoin is DELETE /v1/groups/{id}/join (user). Leaves a group joined free; a bought membership stays.

func (*Client) DeleteGroupMembers added in v0.83.0

func (c *Client) DeleteGroupMembers(ctx context.Context, id string, body []groups.Membership) (groups.Written, error)

DeleteGroupMembers is DELETE /v1/groups/{id}/members (staff). Removes contents or versions from a group.

func (*Client) DeleteGroupNames added in v0.83.0

func (c *Client) DeleteGroupNames(ctx context.Context, id string, body []groups.Name) ([]groups.Name, error)

DeleteGroupNames is DELETE /v1/groups/{id}/names (staff). Removes names and aliases from a group.

func (*Client) DeleteGroupOffer added in v0.83.0

func (c *Client) DeleteGroupOffer(ctx context.Context, id string) (offers.Offer, error)

DeleteGroupOffer is DELETE /v1/groups/{id}/offer (editor). Withdraws a group's offer; members keep theirs, subscribers until their paid period ends: no subscription renews. For the group's owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope and staff holding root:offers:write.

func (*Client) DeleteGroupReaction added in v0.83.0

func (c *Client) DeleteGroupReaction(ctx context.Context, id string) (content.ReactionCounts, error)

DeleteGroupReaction is DELETE /v1/groups/{id}/reaction (public). Clears the caller's reaction to a group.

func (*Client) DeleteMeHistory added in v0.83.0

func (c *Client) DeleteMeHistory(ctx context.Context, q DeleteMeHistoryQuery) error

DeleteMeHistory is DELETE /v1/me/history (user). Erases the reader's history: of one content item, one kind or everything.

func (*Client) DeleteMeSearches added in v0.83.0

func (c *Client) DeleteMeSearches(ctx context.Context, q DeleteMeSearchesQuery) error

DeleteMeSearches is DELETE /v1/me/searches (public). Clears the reader's recent searches: one query, or every one so far.

func (*Client) DeleteModerationBannedWords added in v0.83.0

func (c *Client) DeleteModerationBannedWords(ctx context.Context, body []string) error

DeleteModerationBannedWords is DELETE /v1/moderation/banned-words (staff). Unbans words.

func (*Client) DeleteModerationIpBans added in v0.83.0

func (c *Client) DeleteModerationIpBans(ctx context.Context, body []string) error

DeleteModerationIpBans is DELETE /v1/moderation/ip-bans (staff). Lifts the bans of addresses or networks.

func (*Client) DeleteNotificationDismiss added in v0.83.0

func (c *Client) DeleteNotificationDismiss(ctx context.Context, id string) (notify.Item, error)

DeleteNotificationDismiss is DELETE /v1/notifications/{id}/dismiss (user). Restores an archived receipt to this user's inbox.

func (*Client) DeleteNotificationRead added in v0.83.0

func (c *Client) DeleteNotificationRead(ctx context.Context, id string) (notify.Item, error)

DeleteNotificationRead is DELETE /v1/notifications/{id}/read (user). Marks one of the signed-in person's notices unread locally.

func (*Client) DeletePoll added in v0.83.0

func (c *Client) DeletePoll(ctx context.Context, id string) error

DeletePoll is DELETE /v1/polls/{id} (staff). Deletes a poll and its media folder.

func (*Client) DeletePollOption added in v0.83.0

func (c *Client) DeletePollOption(ctx context.Context, id string, oid string) error

DeletePollOption is DELETE /v1/polls/{id}/options/{oid} (staff). Removes an option and its votes; a poll keeps at least two.

func (*Client) GetCatalogTotals added in v0.83.0

func (c *Client) GetCatalogTotals(ctx context.Context, q GetCatalogTotalsQuery) (catalog.Totals, error)

GetCatalogTotals is GET /v1/catalog/totals (open). Each kind's content, in all and per language: how many items an anonymous visitor's listing shows and opens, and how many each tier's holders open. The request's site scope applies. Cacheable for ten minutes.

func (*Client) GetCode added in v0.83.0

func (c *Client) GetCode(ctx context.Context, code string, q GetCodeQuery) (contenturl.Resolved, error)

GetCode is GET /v1/codes/{code} (open). A content code's link and canonical path; any spelling of the code resolves.

func (*Client) GetCommentBans added in v0.83.0

func (c *Client) GetCommentBans(ctx context.Context, q GetCommentBansQuery) ([]content.CommentBan, error)

GetCommentBans is GET /v1/comment-bans (user). The bans the caller placed on commenters of its own content, newest first, expired ones included.

func (*Client) GetCommentReplies added in v0.83.0

func (c *Client) GetCommentReplies(ctx context.Context, cid string, q GetCommentRepliesQuery) ([]content.Comment, error)

GetCommentReplies is GET /v1/comments/{cid}/replies (public). A comment's replies, oldest first; withheld (empty) where the caller can't open the content.

func (*Client) GetCommentsAdmin added in v0.83.0

func (c *Client) GetCommentsAdmin(ctx context.Context, q GetCommentsAdminQuery) ([]content.AdminComment, error)

GetCommentsAdmin is GET /v1/comments/admin (staff). Every comment, newest first, deleted, held and rejected ones with their real bodies.

func (*Client) GetCommentsLatest added in v0.83.0

func (c *Client) GetCommentsLatest(ctx context.Context, q GetCommentsLatestQuery) ([]content.FeedItem, error)

GetCommentsLatest is GET /v1/comments/latest (public). The newest published comments across the tenant, with their targets; a page may under-fill.

func (*Client) GetConfig added in v0.83.0

func (c *Client) GetConfig(ctx context.Context) (content.Config, error)

GetConfig is GET /v1/config (open). What the content module allows: which interactions signed-out visitors may make, and the longest comment.

func (*Client) GetContent added in v0.83.0

func (c *Client) GetContent(ctx context.Context, id string, q GetContentQuery) (catalog.Content, error)

GetContent is GET /v1/contents/{id} (public). A content item: its text in the viewer's language, live versions with their segments and access, groups and attributes; the body only to viewers who open a version, everyone else its description and the paywall decision. Editors see every version, every language and the moderation state.

func (*Client) GetContentCanComment added in v0.83.0

func (c *Client) GetContentCanComment(ctx context.Context, id string, q GetContentCanCommentQuery) (content.CommentStanding, error)

GetContentCanComment is GET /v1/contents/{id}/can-comment (public). Whether the caller may comment on a content item or one of its versions, and the ban that stops it.

func (*Client) GetContentComments added in v0.83.0

func (c *Client) GetContentComments(ctx context.Context, id string, q GetContentCommentsQuery) ([]content.Comment, error)

GetContentComments is GET /v1/contents/{id}/comments (public). The top-level comments on a content item or one of its versions with reply counts; the caller also sees its own held and rejected ones. Withheld (empty) where the caller can't open the content; the standing says so.

func (*Client) GetContentFavorite added in v0.83.0

func (c *Client) GetContentFavorite(ctx context.Context, id string, q GetContentFavoriteQuery) (content.FavoriteState, error)

GetContentFavorite is GET /v1/contents/{id}/favorite (public). The favorite count of a content item or one of its versions and whether the caller favorited it (never, signed out: then it must be visible).

func (*Client) GetContentGroups added in v0.83.0

func (c *Client) GetContentGroups(ctx context.Context, id string, q GetContentGroupsQuery) (groups.ContentMemberships, error)

GetContentGroups is GET /v1/contents/{id}/groups (staff). A content's memberships, its versions' included, with the groups they name.

func (*Client) GetContentReaction added in v0.83.0

func (c *Client) GetContentReaction(ctx context.Context, id string, q GetContentReactionQuery) (content.ReactionCounts, error)

GetContentReaction is GET /v1/contents/{id}/reaction (public). The like and dislike counts of a content item or one of its versions and the caller's own reaction.

func (*Client) GetContentSimilar added in v0.83.0

func (c *Client) GetContentSimilar(ctx context.Context, id string, q GetContentSimilarQuery) (discovery.ContentPage, error)

GetContentSimilar is GET /v1/contents/{id}/similar (public). Contents like this one (co-engagement, or the configured source), of its kind unless kind names another.

func (*Client) GetContentVersion added in v0.83.0

func (c *Client) GetContentVersion(ctx context.Context, id string, vid string, q GetContentVersionQuery) (catalog.Version, error)

GetContentVersion is GET /v1/contents/{id}/versions/{vid} (public). One version of a content item as the viewer sees it, with its segments and access decision.

func (*Client) GetContentVersionDownload added in v0.83.0

func (c *Client) GetContentVersionDownload(ctx context.Context, id string, version string) (*Reply, error)

GetContentVersionDownload is GET /v1/contents/{id}/versions/{version}/download (public). A version's pages as a zip: every segment released and the version open. Charged to the viewer's download quota; marked by the host's watermark. 202 while it is prepared.

func (*Client) GetContentVersionReader added in v0.83.0

func (c *Client) GetContentVersionReader(ctx context.Context, id string, version string) (*Reply, error)

GetContentVersionReader is GET /v1/contents/{id}/versions/{version}/reader (public). A version's reader plan: its segments with their release state, the published item of each the viewer opens, the previews of a locked version, its playback item and reader metadata. 202 while it has no current publication.

func (*Client) GetContents added in v0.83.0

func (c *Client) GetContents(ctx context.Context, q GetContentsQuery) (discovery.ContentPage, error)

GetContents is GET /v1/contents (public). Lists contents as cards, one per content item with the version that fits the reader, under their visibility, site and content filters.

func (*Client) GetGlobalCommentBan added in v0.83.0

func (c *Client) GetGlobalCommentBan(ctx context.Context, user string) (content.CommentBan, error)

GetGlobalCommentBan is GET /v1/global-comment-bans/{user} (staff). A user's tenant-wide comment ban, expired or not; 404 when they have none.

func (*Client) GetGlobalCommentBans added in v0.83.0

func (c *Client) GetGlobalCommentBans(ctx context.Context, q GetGlobalCommentBansQuery) ([]content.CommentBan, error)

GetGlobalCommentBans is GET /v1/global-comment-bans (staff). The tenant-wide comment bans, newest first, expired ones included.

func (*Client) GetGroup added in v0.83.0

func (c *Client) GetGroup(ctx context.Context, id string, q GetGroupQuery) (groups.GroupDetail, error)

GetGroup is GET /v1/groups/{id} (public). An active group with its names, text, attributes, parent, scope, children, cover, avatar, edges, counts and sale; a merged group answers with its survivor.

func (*Client) GetGroupCanComment added in v0.83.0

func (c *Client) GetGroupCanComment(ctx context.Context, id string) (content.CommentStanding, error)

GetGroupCanComment is GET /v1/groups/{id}/can-comment (public). Whether the caller may comment on a group, and the ban that stops it.

func (*Client) GetGroupCommentBans added in v0.83.0

func (c *Client) GetGroupCommentBans(ctx context.Context, id string, q GetGroupCommentBansQuery) ([]content.CommentBan, error)

GetGroupCommentBans is GET /v1/groups/{id}/comment-bans (user). The bans on commenting on a user-owned group and the content it owns, newest first, expired ones included: for root:comments:ban holders and the group's comment moderators.

func (*Client) GetGroupComments added in v0.83.0

func (c *Client) GetGroupComments(ctx context.Context, id string, q GetGroupCommentsQuery) ([]content.Comment, error)

GetGroupComments is GET /v1/groups/{id}/comments (public). The top-level comments on a group with reply counts; the caller also sees its own held and rejected ones.

func (*Client) GetGroupFavorite added in v0.83.0

func (c *Client) GetGroupFavorite(ctx context.Context, id string) (content.FavoriteState, error)

GetGroupFavorite is GET /v1/groups/{id}/favorite (public). The favorite count of a group and whether the caller favorited it (never, signed out: then it must be visible).

func (*Client) GetGroupHeldComments added in v0.83.0

func (c *Client) GetGroupHeldComments(ctx context.Context, id string, q GetGroupHeldCommentsQuery) (content.HeldPage, error)

GetGroupHeldComments is GET /v1/groups/{id}/held-comments (user). Held comments on a user-owned group and the content it owns, oldest first: for root:moderation:review holders and the group's comment moderators.

func (*Client) GetGroupMembers added in v0.83.0

func (c *Client) GetGroupMembers(ctx context.Context, id string, q GetGroupMembersQuery) (groups.MemberPage, error)

GetGroupMembers is GET /v1/groups/{id}/members (staff). A group's memberships in member order, paged.

func (*Client) GetGroupReaction added in v0.83.0

func (c *Client) GetGroupReaction(ctx context.Context, id string) (content.ReactionCounts, error)

GetGroupReaction is GET /v1/groups/{id}/reaction (public). The like and dislike counts of a group and the caller's own reaction.

func (*Client) GetGroups added in v0.83.0

func (c *Client) GetGroups(ctx context.Context, q GetGroupsQuery) (groups.GroupPage, error)

GetGroups is GET /v1/groups (public). Lists active groups with name, code, path, cover, avatar, count and a sellable group's offer and the viewer's membership, filtered, ordered and cursor-paged.

func (*Client) GetKinds added in v0.83.0

func (c *Client) GetKinds(ctx context.Context) (kindsroute.ContentModel, error)

GetKinds is GET /v1/kinds (open). The site's content model: languages, tiers, content kinds, group kinds, content filters and whether signals are recorded. Cacheable for five minutes.

func (*Client) GetManageContents added in v0.83.0

func (c *Client) GetManageContents(ctx context.Context, q GetManageContentsQuery) ([]catalog.Content, error)

GetManageContents is GET /v1/manage/contents (public). Content items to manage, newest first: drafts, scheduled, held and live ones; deleted ones on their own. root:content:write holders see all; others what they created, or an owner group's they edit.

func (*Client) GetMeContentFilters added in v0.83.0

func (c *Client) GetMeContentFilters(ctx context.Context, q GetMeContentFiltersQuery) (discovery.ContentFilters, error)

GetMeContentFilters is GET /v1/me/content-filters (user). The reader's content filters: each configured filter's group, default and setting; every read applies them.

func (*Client) GetMeFavorites added in v0.83.0

GetMeFavorites is GET /v1/me/favorites (user). The content items (as their listing shows them) or groups the reader favorited, most recent first.

func (*Client) GetMeGroups added in v0.83.0

func (c *Client) GetMeGroups(ctx context.Context, q GetMeGroupsQuery) (groups.MyGroupPage, error)

GetMeGroups is GET /v1/me/groups (user). The viewer's active groups: those they own, edit ({kind}:settings:manage), write content of ({kind}:content:write) or moderate comments of ({kind}:comments:moderate) through the group's AuthKit scope, or hold the key of, each saying which, as GET /groups lists them.

func (*Client) GetMeHistory added in v0.83.0

func (c *Client) GetMeHistory(ctx context.Context, q GetMeHistoryQuery) (discovery.ContentPage, error)

GetMeHistory is GET /v1/me/history (user). The contents the reader engaged with, most recent first, with their progress.

func (*Client) GetMeLibrary added in v0.83.0

func (c *Client) GetMeLibrary(ctx context.Context, q GetMeLibraryQuery) (offers.LibraryPage, error)

GetMeLibrary is GET /v1/me/library (user). The content the viewer bought, newest first, deleted content included.

func (*Client) GetMeReactions added in v0.83.0

GetMeReactions is GET /v1/me/reactions (user). The content items or groups the reader liked or disliked, most recent first: tag affinities and preferences.

func (*Client) GetMeSearches added in v0.83.0

GetMeSearches is GET /v1/me/searches (public). The reader's recent searches, newest first, since they last cleared them; a signed-out reader names its session.

func (*Client) GetMeStats added in v0.85.0

func (c *Client) GetMeStats(ctx context.Context) (discovery.MyStats, error)

GetMeStats is GET /v1/me/stats (user). The signed-in reader's account statistics: the contents they finished per kind, their comments and the reactions those received.

func (*Client) GetMedia added in v0.83.0

func (c *Client) GetMedia(ctx context.Context, kind string, id string, q GetMediaQuery) (media.ReadResult, error)

GetMedia is GET /v1/media/{kind}/{id} (public). An item's files under a prefix in manifest order: with access each has a URL (and the item cookie is set), without each is locked.

func (*Client) GetMediaHls added in v0.83.0

func (c *Client) GetMediaHls(ctx context.Context, kind string, id string, path string, q GetMediaHlsQuery) (*Reply, error)

GetMediaHls is GET /v1/media/{kind}/{id}/hls/{path...} (public). HLS: {dir}master.m3u8 (a ladder of the read's hls), {path}.m3u8 (a track) or {dir}sprite.vtt (the seek sprite).

func (*Client) GetMediaPresets added in v0.83.0

func (c *Client) GetMediaPresets(ctx context.Context) ([]media.PresetRule, error)

GetMediaPresets is GET /v1/media/presets (open). Every kind's public preset rules: crop bounds, widths and default images. Current image URLs come from a read's public.

func (*Client) GetMediaUploadFrame added in v0.83.0

func (c *Client) GetMediaUploadFrame(ctx context.Context, q GetMediaUploadFrameQuery) (*Reply, error)

GetMediaUploadFrame is GET /v1/media/upload/frame (editor). A JPEG still of a video upload, for the frame picker (UploadOptions.Frames).

func (*Client) GetMediaVersion added in v0.83.0

func (c *Client) GetMediaVersion(ctx context.Context, kind string, id string, version string, q GetMediaVersionQuery) (media.ReadResult, error)

GetMediaVersion is GET /v1/media/{kind}/{id}/versions/{version} (public). A version's item, read as GET /{kind}/{id} reads a content-level or unversioned item.

func (*Client) GetMediaVersionHls added in v0.83.0

func (c *Client) GetMediaVersionHls(ctx context.Context, kind string, id string, version string, path string, q GetMediaVersionHlsQuery) (*Reply, error)

GetMediaVersionHls is GET /v1/media/{kind}/{id}/versions/{version}/hls/{path...} (public). A version's HLS playlists, as GET /{kind}/{id}/hls/{path...} serves an item's.

func (*Client) GetModerationBannedWords added in v0.83.0

func (c *Client) GetModerationBannedWords(ctx context.Context) ([]content.BannedWord, error)

GetModerationBannedWords is GET /v1/moderation/banned-words (staff). The banned words and what each does.

func (*Client) GetModerationHeld added in v0.83.0

func (c *Client) GetModerationHeld(ctx context.Context, q GetModerationHeldQuery) (content.HeldPage, error)

GetModerationHeld is GET /v1/moderation/held (staff). Held comments or content items awaiting review, oldest first.

func (*Client) GetModerationIpBans added in v0.83.0

func (c *Client) GetModerationIpBans(ctx context.Context) ([]content.IPBan, error)

GetModerationIpBans is GET /v1/moderation/ip-bans (staff). The IP bans, newest first, expired ones included.

func (*Client) GetNotificationsInbox added in v0.83.0

func (c *Client) GetNotificationsInbox(ctx context.Context, q GetNotificationsInboxQuery) (notify.Page, error)

GetNotificationsInbox is GET /v1/notifications/inbox (user). The signed-in person's inbox, newest first.

func (*Client) GetOffers added in v0.83.0

func (c *Client) GetOffers(ctx context.Context, q GetOffersQuery) (offers.OfferPage, error)

GetOffers is GET /v1/offers (staff). Offers, newest first, with their OpenRails sync state.

func (*Client) GetPoll added in v0.83.0

func (c *Client) GetPoll(ctx context.Context, id string) (content.Poll, error)

GetPoll is GET /v1/polls/{id} (public). A poll with the caller's vote or answer; a scheduled or inactive poll only for root:polls:write holders.

func (*Client) GetPolls added in v0.83.0

func (c *Client) GetPolls(ctx context.Context, q GetPollsQuery) ([]content.Poll, error)

GetPolls is GET /v1/polls (public). Live polls, newest first.

func (*Client) GetPollsAdmin added in v0.83.0

func (c *Client) GetPollsAdmin(ctx context.Context, q GetPollsAdminQuery) ([]content.Poll, error)

GetPollsAdmin is GET /v1/polls/admin (staff). Every poll, scheduled and inactive ones included.

func (*Client) GetRecommendations added in v0.83.0

func (c *Client) GetRecommendations(ctx context.Context, q GetRecommendationsQuery) (discovery.ContentPage, error)

GetRecommendations is GET /v1/recommendations (user). Contents for the signed-in reader from their history, filled from what is popular; seen and disliked content left out.

func (*Client) GetReleaseDates added in v0.83.0

func (c *Client) GetReleaseDates(ctx context.Context, q GetReleaseDatesQuery) (discovery.ReleaseDates, error)

GetReleaseDates is GET /v1/release-dates (public). The release date index: per day or month, newest first, the contents the reader's listing shows released then and how many are free.

func (*Client) GetSearch added in v0.83.0

GetSearch is GET /v1/search (public). Keyword search over contents, grouped per content item, personalized for a signed-in reader unless sorted otherwise.

func (*Client) GetSitemapContents added in v0.85.0

func (c *Client) GetSitemapContents(ctx context.Context, q GetSitemapContentsQuery) (discovery.SitemapPage, error)

GetSitemapContents is GET /v1/sitemap/contents (open). Pages through the site's public content pages of one kind, by id, for host sitemaps: live, listed and in the request's site scope, each with every language it serves.

func (*Client) GetSitemapGroups added in v0.85.0

func (c *Client) GetSitemapGroups(ctx context.Context, q GetSitemapGroupsQuery) (discovery.SitemapPage, error)

GetSitemapGroups is GET /v1/sitemap/groups (open). Pages through the site's public group pages of one kind, by id, for host sitemaps: active groups holding content the site lists, each with the languages that content serves.

func (*Client) GetStatsActivity added in v0.85.0

func (c *Client) GetStatsActivity(ctx context.Context, q GetStatsActivityQuery) (discovery.Activity, error)

GetStatsActivity is GET /v1/stats/activity (staff). A content kind's views per UTC day, newest first: view sessions, distinct viewers and signed-in viewers, from the signal plane.

func (*Client) GetStatsTop added in v0.85.0

func (c *Client) GetStatsTop(ctx context.Context, q GetStatsTopQuery) (discovery.TopContents, error)

GetStatsTop is GET /v1/stats/top (staff). A content kind's live, listed content in the request's site ranked by views (the signal plane's) or published comments over a period.

func (*Client) GetTypeahead added in v0.83.0

func (c *Client) GetTypeahead(ctx context.Context, q GetTypeaheadQuery) (discovery.Typeahead, error)

GetTypeahead is GET /v1/typeahead (public). Suggests contents and groups while the reader types, tolerating typos and partial words.

func (*Client) IngestMedia added in v0.83.0

func (c *Client) IngestMedia(ctx context.Context, bucket *http.Client, ref media.RefBody, operationID string, files []MediaFile) (media.CommitReply, error)

IngestMedia uploads files into one media item (a content item's, or one version's) and commits them as one operation: the upload routes' presign, a PUT or multipart parts to the bucket over bucket (http.DefaultClient when nil), then commit. operationID is a UUID of this commit; sending the files again (a new operationID) finds bytes the folder holds and uploads nothing twice. Files with From are copied server-side, in one request. It is how an importer fills an imported version's folder.

func (*Client) PatchComment added in v0.83.0

func (c *Client) PatchComment(ctx context.Context, cid string, body content.CommentEdit) (content.Comment, error)

PatchComment is PATCH /v1/comments/{cid} (public). Edits a comment: its author, or a root:comments:moderate holder. 202 when the moderator holds the new text.

func (*Client) PatchContent added in v0.83.0

func (c *Client) PatchContent(ctx context.Context, id string, body catalog.ContentPatch) (catalog.Content, error)

PatchContent is PATCH /v1/contents/{id} (public). Edits a content item's listing, access, restrictions, attributes, cover or creators. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PatchContentVersion added in v0.83.0

func (c *Client) PatchContentVersion(ctx context.Context, id string, vid string, body catalog.VersionInput) (catalog.Version, error)

PatchContentVersion is PATCH /v1/contents/{id}/versions/{vid} (public). Edits a version: languages, traits, label, state and schedule, access, attributes, text and segments. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PatchGroup added in v0.83.0

func (c *Client) PatchGroup(ctx context.Context, id string, body groups.GroupUpdate) (groups.Group, error)

PatchGroup is PATCH /v1/groups/{id} (editor). Updates a group's given fields; state restores or deletes it. Staff holding root:groups:write write any group; a signed-in person creates groups of user-owned kinds, as their owner and within the kind's max_per_owner, and the owner and holders of {kind}:settings:manage in its AuthKit scope edit names, text, attributes, cover, avatar and state.

func (*Client) PatchPoll added in v0.83.0

func (c *Client) PatchPoll(ctx context.Context, id string, body content.PollUpdate) (content.Poll, error)

PatchPoll is PATCH /v1/polls/{id} (staff). Updates a poll's given fields; closes_at null reopens it until deactivated.

func (*Client) PatchPollOption added in v0.83.0

func (c *Client) PatchPollOption(ctx context.Context, id string, oid string, body content.PollOptionPatch) (content.PollOption, error)

PatchPollOption is PATCH /v1/polls/{id}/options/{oid} (staff). Updates an option's label or position.

func (*Client) PostCodesAliases added in v0.83.0

PostCodesAliases is POST /v1/codes/aliases (open). Resolves legacy identifiers (the import's aliases) to their content's link, canonical path and locator, in order, following merges: found, not_found (unknown or hidden) or gone; at most 100.

func (*Client) PostCommentDislike added in v0.83.0

func (c *Client) PostCommentDislike(ctx context.Context, cid string) (content.ReactionCounts, error)

PostCommentDislike is POST /v1/comments/{cid}/dislike (public). Dislikes a comment.

func (*Client) PostCommentHold added in v0.83.0

func (c *Client) PostCommentHold(ctx context.Context, cid string) error

PostCommentHold is POST /v1/comments/{cid}/hold (user). Holds a published comment for review: out of its thread, shown to its author only, until released or deleted. For root:comments:moderate holders and, on a user-owned group or its content, the group's comment moderators (its owner, holders of {kind}:comments:moderate in its AuthKit scope).

func (*Client) PostCommentLike added in v0.83.0

func (c *Client) PostCommentLike(ctx context.Context, cid string) (content.ReactionCounts, error)

PostCommentLike is POST /v1/comments/{cid}/like (public). Likes a comment.

func (*Client) PostCommentRelease added in v0.83.0

func (c *Client) PostCommentRelease(ctx context.Context, cid string) error

PostCommentRelease is POST /v1/comments/{cid}/release (user). Publishes a held comment, as a reviewer's approval does. For root:comments:moderate holders and, on a user-owned group or its content, the group's comment moderators (its owner, holders of {kind}:comments:moderate in its AuthKit scope).

func (*Client) PostCommentRestore added in v0.83.0

func (c *Client) PostCommentRestore(ctx context.Context, cid string) (content.Restored, error)

PostCommentRestore is POST /v1/comments/{cid}/restore (staff). Restores a deleted comment.

func (*Client) PostContentComments added in v0.83.0

func (c *Client) PostContentComments(ctx context.Context, id string, body content.CommentInput, q PostContentCommentsQuery) (content.Comment, error)

PostContentComments is POST /v1/contents/{id}/comments (public). Comments on a content item or one of its versions in a language's thread, or replies to a top-level comment; a signed-out caller gives anon_name, where Config.anonymous.comments allows it. 202 when the moderator holds it.

func (*Client) PostContentDislike added in v0.83.0

func (c *Client) PostContentDislike(ctx context.Context, id string, q PostContentDislikeQuery) (content.ReactionCounts, error)

PostContentDislike is POST /v1/contents/{id}/dislike (public). Dislikes a content item or one of its versions.

func (*Client) PostContentFavorite added in v0.83.0

func (c *Client) PostContentFavorite(ctx context.Context, id string, q PostContentFavoriteQuery) (content.FavoriteState, error)

PostContentFavorite is POST /v1/contents/{id}/favorite (user). Favorites a content item or one of its versions the caller sees; favoriting again is a no-op. Answers the state with its favorite count.

func (*Client) PostContentImages added in v0.83.0

func (c *Client) PostContentImages(ctx context.Context, id string, body content.ImageInput) (catalog.InlineImage, error)

PostContentImages is POST /v1/contents/{id}/images (public). The reference to store in a body for an inline image uploaded to the item's media folder, and a URL that shows it to the editor now. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PostContentLike added in v0.83.0

func (c *Client) PostContentLike(ctx context.Context, id string, q PostContentLikeQuery) (content.ReactionCounts, error)

PostContentLike is POST /v1/contents/{id}/like (public). Likes a content item or one of its versions.

func (*Client) PostContentPublish added in v0.83.0

func (c *Client) PostContentPublish(ctx context.Context, id string, body catalog.PublishInput) (catalog.Content, error)

PostContentPublish is POST /v1/contents/{id}/publish (public). Publishes a content item now, or schedules it for publish_at; 202 when the moderator holds it for review. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PostContentRestore added in v0.83.0

func (c *Client) PostContentRestore(ctx context.Context, id string) (catalog.Content, error)

PostContentRestore is POST /v1/contents/{id}/restore (public). Restores a deleted content item as it was. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PostContentUnpublish added in v0.83.0

func (c *Client) PostContentUnpublish(ctx context.Context, id string) (catalog.Content, error)

PostContentUnpublish is POST /v1/contents/{id}/unpublish (public). Returns a published or scheduled content item to a draft. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PostContentVersionFork added in v0.83.0

func (c *Client) PostContentVersionFork(ctx context.Context, id string, version string, body versions.ForkBody) (versions.Pending, error)

PostContentVersionFork is POST /v1/contents/{id}/versions/{version}/fork (user). Fills a version without media from a sibling (editors): its uploads with their outputs, reader metadata and optionally its audio and segments, in a durable job.

func (*Client) PostContentVersions added in v0.83.0

func (c *Client) PostContentVersions(ctx context.Context, id string, body catalog.VersionInput) (catalog.Version, error)

PostContentVersions is POST /v1/contents/{id}/versions (public). Adds a version after the others. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PostContents added in v0.83.0

func (c *Client) PostContents(ctx context.Context, body catalog.ContentInput) (catalog.Content, error)

PostContents is POST /v1/contents (public). Creates a content item as a draft. root:content:write holders, or an owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) in it.

func (*Client) PostExposures added in v0.83.0

func (c *Client) PostExposures(ctx context.Context, body discovery.Exposures) error

PostExposures is POST /v1/exposures (public). Records result lists a client served, rendered or showed, for click attribution.

func (*Client) PostGroupComments added in v0.83.0

func (c *Client) PostGroupComments(ctx context.Context, id string, body content.CommentInput) (content.Comment, error)

PostGroupComments is POST /v1/groups/{id}/comments (public). Comments on a group in a language's thread, or replies to a top-level comment; a signed-out caller gives anon_name, where Config.anonymous.comments allows it. 202 when the moderator holds it.

func (*Client) PostGroupDislike added in v0.83.0

func (c *Client) PostGroupDislike(ctx context.Context, id string) (content.ReactionCounts, error)

PostGroupDislike is POST /v1/groups/{id}/dislike (public). Dislikes a group.

func (*Client) PostGroupEdges added in v0.83.0

func (c *Client) PostGroupEdges(ctx context.Context, id string, body []groups.EdgeInput) ([]groups.Edge, error)

PostGroupEdges is POST /v1/groups/{id}/edges (staff). Adds edges from a group.

func (*Client) PostGroupFavorite added in v0.83.0

func (c *Client) PostGroupFavorite(ctx context.Context, id string) (content.FavoriteState, error)

PostGroupFavorite is POST /v1/groups/{id}/favorite (user). Favorites a group the caller sees; favoriting again is a no-op. Answers the state with its favorite count.

func (*Client) PostGroupJoin added in v0.83.0

func (c *Client) PostGroupJoin(ctx context.Context, id string) (offers.GroupJoin, error)

PostGroupJoin is POST /v1/groups/{id}/join (user). Joins a group free to join: grants the viewer its key.

func (*Client) PostGroupLike added in v0.83.0

func (c *Client) PostGroupLike(ctx context.Context, id string) (content.ReactionCounts, error)

PostGroupLike is POST /v1/groups/{id}/like (public). Likes a group.

func (*Client) PostGroupMembers added in v0.83.0

func (c *Client) PostGroupMembers(ctx context.Context, id string, body []groups.Membership) (groups.Written, error)

PostGroupMembers is POST /v1/groups/{id}/members (staff). Adds contents or versions to a group, or updates their position, window and state.

func (*Client) PostGroupMerge added in v0.83.0

func (c *Client) PostGroupMerge(ctx context.Context, id string, body groups.MergeInput) (groups.MergeReport, error)

PostGroupMerge is POST /v1/groups/{id}/merge (staff). Merges a group into another of its kind: memberships, names, edges and children move, and its code redirects.

func (*Client) PostGroupNames added in v0.83.0

func (c *Client) PostGroupNames(ctx context.Context, id string, body []groups.Name) ([]groups.Name, error)

PostGroupNames is POST /v1/groups/{id}/names (staff). Adds names and aliases to a group; a new canonical name demotes the old one to an alias.

func (*Client) PostGroups added in v0.83.0

func (c *Client) PostGroups(ctx context.Context, body []groups.GroupInput) ([]groups.Group, error)

PostGroups is POST /v1/groups (editor). Creates groups with their names and text; an auth_scope kind's gets its AuthKit scope. Staff holding root:groups:write write any group; a signed-in person creates groups of user-owned kinds, as their owner and within the kind's max_per_owner, and the owner and holders of {kind}:settings:manage in its AuthKit scope edit names, text, attributes, cover, avatar and state.

func (*Client) PostImport added in v0.83.0

func (c *Client) PostImport(ctx context.Context, body importer.Batch) (importer.Result, error)

PostImport is POST /v1/import (staff). Imports a batch of groups, content items, memberships, offers, aliases, comments, reactions, favorites, polls and signals, idempotently by external id: re-sending converges. Records that fail are reported and the rest written; their rebuild jobs are queued.

func (*Client) PostMediaUploadAbort added in v0.83.0

func (c *Client) PostMediaUploadAbort(ctx context.Context, body media.TicketBody) error

PostMediaUploadAbort is POST /v1/media/upload/abort (editor). Abandons a multipart upload.

func (*Client) PostMediaUploadCommit added in v0.83.0

func (c *Client) PostMediaUploadCommit(ctx context.Context, body media.CommitBody) (media.CommitReply, error)

PostMediaUploadCommit is POST /v1/media/upload/commit (editor). Applies ops to an item in one conditional write, then queues placement and processing; answers its uploads as an editor reads them.

func (*Client) PostMediaUploadComplete added in v0.83.0

func (c *Client) PostMediaUploadComplete(ctx context.Context, body media.TicketBody) (media.CompleteReply, error)

PostMediaUploadComplete is POST /v1/media/upload/complete (editor). Assembles a multipart upload; blob is its staged name.

func (*Client) PostMediaUploadCopy added in v0.83.0

func (c *Client) PostMediaUploadCopy(ctx context.Context, body media.CopyBody) (media.CopyReply, error)

PostMediaUploadCopy is POST /v1/media/upload/copy (staff). Stages server-side copies of keys of the item's bucket outside ContentKit's namespaces, for one commit's puts (an importer's files).

func (*Client) PostMediaUploadParts added in v0.83.0

func (c *Client) PostMediaUploadParts(ctx context.Context, body media.PartsBody) (media.PartsReply, error)

PostMediaUploadParts is POST /v1/media/upload/parts (editor). Presigns multipart parts, each bound to its SHA-256.

func (*Client) PostMediaUploadPartsList added in v0.83.0

func (c *Client) PostMediaUploadPartsList(ctx context.Context, body media.TicketBody) (media.PartsReply, error)

PostMediaUploadPartsList is POST /v1/media/upload/parts/list (editor). The parts of a multipart upload that landed, to resume it.

func (*Client) PostMediaUploadPresign added in v0.83.0

func (c *Client) PostMediaUploadPresign(ctx context.Context, body media.PresignBody) (media.PresignReply, error)

PostMediaUploadPresign is POST /v1/media/upload/presign (editor). Plans one upload after checking its path's rules and CanUpload: exists (commit directly), one PUT, or a multipart ticket.

func (*Client) PostModerationBannedWords added in v0.83.0

func (c *Client) PostModerationBannedWords(ctx context.Context, body []content.BannedWordInput) ([]content.BannedWord, error)

PostModerationBannedWords is POST /v1/moderation/banned-words (staff). Bans words, or changes what banned ones do: mask (the default) or reject.

func (*Client) PostModerationIpBans added in v0.83.0

func (c *Client) PostModerationIpBans(ctx context.Context, body []content.IPBanInput) ([]content.IPBan, error)

PostModerationIpBans is POST /v1/moderation/ip-bans (staff). Bans addresses or networks from commenting and answering polls, or replaces their bans.

func (*Client) PostModerationResolve added in v0.83.0

func (c *Client) PostModerationResolve(ctx context.Context, kind string, id string, body content.ResolveInput) (content.ReviewOutcome, error)

PostModerationResolve is POST /v1/moderation/{kind}/{id}/resolve (staff). Approves (publishes) or rejects a held comment or content item, at the revision the queue listed.

func (*Client) PostNotificationsPublish added in v0.83.0

func (c *Client) PostNotificationsPublish(ctx context.Context, body notification.Publication) (notification.Published, error)

PostNotificationsPublish is POST /v1/notifications/publish (staff). Publishes notices in one transaction, each once per origin and recipient; a notice to an erased recipient is dropped.

func (*Client) PostPollAnswer added in v0.83.0

func (c *Client) PostPollAnswer(ctx context.Context, id string, body content.PollAnswerInput) (content.Poll, error)

PostPollAnswer is POST /v1/polls/{id}/answer (user). Stores or replaces the caller's answer to an open free-text poll; banned words and IP bans apply.

func (*Client) PostPollOptions added in v0.83.0

func (c *Client) PostPollOptions(ctx context.Context, id string, body content.PollOptionPatch) (content.PollOption, error)

PostPollOptions is POST /v1/polls/{id}/options (staff). Adds an option to a multiple-choice poll, at the end unless position is given.

func (*Client) PostPollVote added in v0.83.0

func (c *Client) PostPollVote(ctx context.Context, id string, body content.PollVote) (content.Poll, error)

PostPollVote is POST /v1/polls/{id}/vote (public). Votes for an option of an open multiple-choice poll, signed out where Config.anonymous.votes allows it; a vote is final, and voting again changes nothing.

func (*Client) PostPolls added in v0.83.0

func (c *Client) PostPolls(ctx context.Context, body content.PollInput) (content.Poll, error)

PostPolls is POST /v1/polls (staff). Creates a poll with its options; a free-text poll needs an AnswerClassifier.

func (*Client) PostSignals added in v0.83.0

func (c *Client) PostSignals(ctx context.Context, body discovery.Signals) error

PostSignals is POST /v1/signals (public). Records views and clicks of the signed-in reader or, signed out, of the session named; a view's progress is scored against its version.

func (*Client) PutCommentBan added in v0.83.0

func (c *Client) PutCommentBan(ctx context.Context, user string, body content.BanInput) (content.CommentBan, error)

PutCommentBan is PUT /v1/comment-bans/{user} (user). Bans a user from commenting on the caller's content, or replaces the ban; no body bans without reason or end.

func (*Client) PutContentCreators added in v0.83.0

func (c *Client) PutContentCreators(ctx context.Context, id string, body catalog.CreatorsInput) (catalog.Content, error)

PutContentCreators is PUT /v1/contents/{id}/creators (public). Replaces a content item's creators. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PutContentGroups added in v0.83.0

PutContentGroups is PUT /v1/contents/{id}/groups (staff). Replaces a content's memberships, its versions' included, in the group kinds named (every kind its kind joins by default).

func (*Client) PutContentOffer added in v0.83.0

func (c *Client) PutContentOffer(ctx context.Context, id string, body offers.Terms) (offers.Offer, error)

PutContentOffer is PUT /v1/contents/{id}/offer (editor). Puts an offer on a content item: its content key opens every version. For creators of the content, editors of its owner group (its owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope) and staff holding root:offers:write.

func (*Client) PutContentText added in v0.83.0

func (c *Client) PutContentText(ctx context.Context, id string, language string, body catalog.Text) (catalog.Content, error)

PutContentText is PUT /v1/contents/{id}/text/{language} (public). Sets a content item's text in one language; 202 when the moderator holds a live item's edit (its last approved text stays public). Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PutContentVersionOffer added in v0.83.0

func (c *Client) PutContentVersionOffer(ctx context.Context, id string, vid string, body offers.Terms) (offers.Offer, error)

PutContentVersionOffer is PUT /v1/contents/{id}/versions/{vid}/offer (editor). Puts an offer on one version: its version key opens it. For creators of the content, editors of its owner group (its owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope) and staff holding root:offers:write.

func (*Client) PutContentVersionReader added in v0.83.0

func (c *Client) PutContentVersionReader(ctx context.Context, id string, version string, body versions.ReaderMeta) error

PutContentVersionReader is PUT /v1/contents/{id}/versions/{version}/reader (user). Sets a version's reader metadata (editors): anchors and audio cues on its pages and audio uploads.

func (*Client) PutContentVersionSegments added in v0.83.0

func (c *Client) PutContentVersionSegments(ctx context.Context, id string, vid string, body catalog.SegmentsInput) (catalog.Version, error)

PutContentVersionSegments is PUT /v1/contents/{id}/versions/{vid}/segments (public). Replaces a version's segments (chapters): ordered ranges, each released at its time. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PutContentVersionsOrder added in v0.83.0

func (c *Client) PutContentVersionsOrder(ctx context.Context, id string, body catalog.OrderInput) (catalog.Content, error)

PutContentVersionsOrder is PUT /v1/contents/{id}/versions/order (public). Orders a content item's versions: ids lists each version that is not deleted, first first. Creators, the owner group's editors (its owner, holders of {kind}:content:write in its AuthKit scope) and root:content:write holders only.

func (*Client) PutGlobalCommentBan added in v0.83.0

func (c *Client) PutGlobalCommentBan(ctx context.Context, user string, body content.BanInput) (content.CommentBan, error)

PutGlobalCommentBan is PUT /v1/global-comment-bans/{user} (staff). Bans a user from commenting anywhere in the tenant, or replaces the ban.

func (*Client) PutGroupCommentBan added in v0.83.0

func (c *Client) PutGroupCommentBan(ctx context.Context, id string, user string, body content.BanInput) (content.CommentBan, error)

PutGroupCommentBan is PUT /v1/groups/{id}/comment-bans/{user} (user). Bans a user from commenting on a user-owned group and the content it owns, or replaces the ban: for root:comments:ban holders and the group's comment moderators.

func (*Client) PutGroupMembersOrder added in v0.83.0

func (c *Client) PutGroupMembersOrder(ctx context.Context, id string, body groups.Order) (groups.Written, error)

PutGroupMembersOrder is PUT /v1/groups/{id}/members/order (staff). Orders an ordered group: the listed members first, in list order.

func (*Client) PutGroupNames added in v0.83.0

func (c *Client) PutGroupNames(ctx context.Context, id string, body []groups.Name) ([]groups.Name, error)

PutGroupNames is PUT /v1/groups/{id}/names (staff). Replaces a group's names and aliases.

func (*Client) PutGroupOffer added in v0.83.0

func (c *Client) PutGroupOffer(ctx context.Context, id string, body offers.Terms) (offers.Offer, error)

PutGroupOffer is PUT /v1/groups/{id}/offer (editor). Puts an offer on a sellable group: its key opens its members and its members-only content. renew_hours makes it a subscription; amount 0 makes it free to join. Terms without renew_hours stop existing subscriptions renewing. For the group's owner, holders of {kind}:offers:write or {kind}:settings:manage in its AuthKit scope and staff holding root:offers:write.

func (*Client) PutMeContentFilters added in v0.83.0

PutMeContentFilters is PUT /v1/me/content-filters (user). Replaces the reader's content filter settings and whether lists include deleted content they hold.

func (*Client) PutNotificationDismiss added in v0.83.0

func (c *Client) PutNotificationDismiss(ctx context.Context, id string) (notify.Item, error)

PutNotificationDismiss is PUT /v1/notifications/{id}/dismiss (user). Archives this receipt without changing its read or workflow state.

func (*Client) PutNotificationRead added in v0.83.0

func (c *Client) PutNotificationRead(ctx context.Context, id string) (notify.Item, error)

PutNotificationRead is PUT /v1/notifications/{id}/read (user). Marks one of the signed-in person's notices read locally.

func (*Client) PutPollImage added in v0.83.0

func (c *Client) PutPollImage(ctx context.Context, id string, body content.ImageInput) (content.PollImage, error)

PutPollImage is PUT /v1/polls/{id}/image (staff). Sets the question image to an inline image uploaded to the poll's media folder; "" clears it.

func (*Client) PutPollOptionImage added in v0.83.0

func (c *Client) PutPollOptionImage(ctx context.Context, id string, oid string, body content.ImageInput) (content.PollImage, error)

PutPollOptionImage is PUT /v1/polls/{id}/options/{oid}/image (staff). Sets an option's image to an inline image uploaded to the poll's media folder; "" clears it.

type Config added in v0.83.0

type Config struct {
	// Tenant is the one tenant this engine serves: the first column of every
	// key and query, and the prefix of every access key. Required.
	Tenant   string         `yaml:"tenant" json:"tenant"`
	Database DatabaseConfig `yaml:"database" json:"database"`
	// Content is the site's content model: languages, tiers, content kinds
	// (commentable and indexed) and group kinds. Required.
	Content kinds.Config `yaml:"content" json:"content"`
	// Interactions are what comments, reactions, favorites and polls allow.
	Interactions InteractionsConfig `yaml:"interactions" json:"interactions"`
	// Media is the media registry and how it is served; nil runs without
	// media (no upload or media route groups, no post and poll images).
	Media *MediaConfig `yaml:"media" json:"media"`
	// ClickHouse is the signal plane: signals, exposures, history,
	// preferences, popularity and recommendations from signals. Unset, or
	// unavailable when New runs, they are off: the content model says so
	// (signals: false), lists sort on what Postgres holds and the
	// contentkit_clickhouse probe fails while a configured one is down.
	ClickHouse ClickHouseConfig `yaml:"clickhouse" json:"clickhouse"`
	// Offers prices what creators and staff put on sale (needs Deps.Billing).
	Offers offers.Config `yaml:"offers" json:"offers"`
	// Jobs sizes the engine's River queue.
	Jobs JobsConfig `yaml:"jobs" json:"jobs"`
}

Config is the engine's configuration: plain data.

func (Config) MediaKinds added in v0.83.0

func (c Config) MediaKinds() (media.Config, error)

MediaKinds is the media registry Config.Media serves, in the tenant's namespace: content kinds' media are their versions', and each group kind gains cover media (media.ForContent). The media worker runs it: cmd/media-worker reads it as JSON (`contentkit media-config` prints it), and a host building its own worker gives it, with its hooks, to media.NewRegistry.

type DatabaseConfig added in v0.83.0

type DatabaseConfig struct {
	// Schema holds ContentKit's tables. Empty is "contentkit".
	Schema string `yaml:"schema" json:"schema"`
	// RiverSchema holds the River tables ContentKit's jobs run in; empty is
	// Schema plus "_river". Start runs ContentKit's own River client there; a
	// host fleet passed with WithRiverClient must use the same schema.
	RiverSchema string `yaml:"river_schema" json:"river_schema"`
}

DatabaseConfig names the Postgres schemas New creates or upgrades; the pool's role owns them. ContentKit never reads or writes another schema.

type DeleteContentFavoriteQuery added in v0.83.0

type DeleteContentFavoriteQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

DeleteContentFavoriteQuery is the query of DeleteContentFavorite.

type DeleteContentReactionQuery added in v0.83.0

type DeleteContentReactionQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

DeleteContentReactionQuery is the query of DeleteContentReaction.

type DeleteMeHistoryQuery added in v0.83.0

type DeleteMeHistoryQuery struct {
	// only this content kind
	Kind string
	// only this content item
	ContentId string
}

DeleteMeHistoryQuery is the query of DeleteMeHistory.

type DeleteMeSearchesQuery added in v0.83.0

type DeleteMeSearchesQuery struct {
	// a signed-out reader's session, which keeps its recent searches
	Session string
	// only this query
	Query string
}

DeleteMeSearchesQuery is the query of DeleteMeSearches.

type DeliveryConfig added in v0.83.0

type DeliveryConfig struct {
	// Mode is "url" (the default) or "cookie".
	Mode string `yaml:"mode" json:"mode"`
	// CookieDomain is the site's registrable domain; required in cookie mode.
	CookieDomain string `yaml:"cookie_domain" json:"cookie_domain"`
	// SigningKey is the media gateway's current token key, "{kid}:{base64
	// secret}"; PreviousKey the one it still verifies. A secret: the server
	// reads it from the environment. Required.
	SigningKey  string `yaml:"signing_key" json:"signing_key"`
	PreviousKey string `yaml:"previous_key" json:"previous_key"`
	// TTL is the minimum token lifetime and Window its rounding; zero takes
	// media's defaults.
	TTL    time.Duration `yaml:"ttl" json:"ttl"`
	Window time.Duration `yaml:"window" json:"window"`
}

DeliveryConfig is how viewers with access present their item token to the media gateway.

type Deps added in v0.83.0

type Deps struct {
	// Postgres is the pool ContentKit's schemas live in. Required.
	Postgres *pgxpool.Pool
	// ClickHouse is the signal plane's connection; nil opens one from
	// Config.ClickHouse.Addr, or runs without the signal plane.
	ClickHouse signal.Conn
	// Redis shares rate limits and issuance counts across replicas; nil
	// counts per process.
	Redis redis.UniversalClient

	// Auth says who a request is (authkit/auth; AuthKit's
	// ak.Authenticator()). It names its root scope (auth.Root), where staff
	// hold access.Permissions(). Required.
	Auth auth.Authenticator
	// Users is the directory (authkit/userinfo; AuthKit's ak.UserInfo()):
	// authors' names. Nil shows authors by id.
	Users userinfo.Lookup
	// Billing is the site's OpenRails merchant (adapters/openrails.Billing
	// over an embedded engine or the remote client): held keys, offers' products
	// and prices, free grants. Nil holds no keys and sells nothing.
	Billing access.Billing
	// Scopes provisions and retires the AuthKit groups of auth_scope group
	// kinds (adapters/authkit.Scopes over an AuthKit client); nil leaves
	// auth_scope to staff.
	Scopes groups.Scopes

	// Store is the media bucket; required with Config.Media.
	Store media.Store
	// PurgePublic hears of public media URLs overwritten or deleted, for a
	// CDN purge.
	PurgePublic func(ctx context.Context, urls []string)

	// Moderator screens comments and content text; nil publishes everything.
	Moderator content.ContentModerator
	// Classifier groups free-text poll answers; nil refuses free-text polls.
	Classifier content.AnswerClassifier
	// ProviderDataEraser erases what a retaining Moderator or Classifier
	// keeps; required with one.
	ProviderDataEraser content.ProviderDataEraser
	// Processor sanitizes comments, BodyProcessor content items' rich-text
	// bodies (keeping "contentkit:" image references); nil strips tags.
	Processor, BodyProcessor content.ContentProcessor
	// Candidates sources similar and recommended content; nil is
	// co-engagement over the signal plane.
	Candidates discovery.Candidates

	// Clock is the time; nil is time.Now.
	Clock func() time.Time
	// Logger; nil is slog.Default().
	Logger *slog.Logger
}

Deps is everything the engine reaches outside its process: connections and providers. The host owns each; Close leaves them open.

type Engine added in v0.83.0

type Engine struct {
	// contains filtered or unexported fields
}

Engine is ContentKit running in this process for one tenant.

func New added in v0.83.0

func New(ctx context.Context, cfg Config, deps Deps) (_ *Engine, err error)

New runs ContentKit in this process. Before anything else touches the database it creates or upgrades ContentKit's tables in Config.Database.Schema, River's in Config.Database.RiverSchema and the media worker's queue; replicas booting together take turns on an advisory lock. With ClickHouse.Addr it applies the signal plane's migrations too; a signal plane it can't open leaves signals off (see openSignalPlane). It starts no jobs: Start does, and jobs queued before it wait. ctx bounds the wait for the databases.

func (*Engine) Client added in v0.83.0

func (e *Engine) Client() *Client

Client is the engine's in-process client, acting as the host itself: holding the Engine is the authority, so it sends no credential and holds every staff permission. Routes for a signed-in person (tier user) refuse it.

func (*Engine) Close added in v0.83.0

func (e *Engine) Close(ctx context.Context) error

Close stops what Start started (ContentKit's own River, never the host's fleet) and closes what New opened, leaving the host's pool, Redis and ClickHouse connections open.

func (*Engine) EraseSubjects added in v0.83.0

func (e *Engine) EraseSubjects(ctx context.Context, subjects []signal.Subject) (signal.ErasureReport, error)

EraseSubjects erases subjects from every plane: signals, interaction data, recent searches, inboxes, people's avatars (media.Config.Avatars) and data retained by moderation and classifier providers; approved authored content stays (content.EraseSubjects). Retry while error != nil or !report.Complete(); Remaining names a plane that could not finish (content_plane, media_plane, signal_plane). Without a signal plane only the content and media planes are erased; a configured one that New could not open stays signal_plane until a restart opens it.

func (*Engine) Probes added in v0.83.0

func (e *Engine) Probes() []Probe

Probes lists the optional dependencies the engine degrades without, for the host's dependency supervisor: the signal plane, the media bucket, Redis and the moderator (failing while its breaker holds writes for review).

func (*Engine) Ready added in v0.83.0

func (e *Engine) Ready(ctx context.Context) error

Ready reports whether the engine can serve: Postgres answers and its jobs run (the host's fleet bound to RiverJobs, or ContentKit's own River since Start). Optional dependencies never fail it (see Probes).

func (*Engine) RiverJobs added in v0.83.0

func (e *Engine) RiverJobs() riverhelpers.Contribution

RiverJobs is ContentKit's contribution to the host's River fleet (its workers, periodic jobs and queues, media's included), for riverhelpers.New in Config.Database.RiverSchema; then pass the fleet to Start with WithRiverClient. Requesting it rules out ContentKit's own River.

func (*Engine) RouteGroups added in v0.83.0

func (e *Engine) RouteGroups() RouteGroups

RouteGroups is every route group the engine can serve: those whose modules its Config provides.

func (*Engine) Routes added in v0.83.0

func (e *Engine) Routes(selection Routes) ([]Route, error)

Routes returns the selected route groups as one subtree, {Prefix}/v1/, for the host's root router (mux.Handle(r.Path, r.Handler) on net/http's ServeMux). It fails, before anything mounts, for a group the engine cannot serve: one whose module its Config leaves out.

func (*Engine) Start added in v0.83.0

func (e *Engine) Start(ctx context.Context, opts ...StartOption) error

Start starts ContentKit's background work: search indexing, preference sync, co-engagement, projection repair, erasure and the media jobs. With no options it builds and runs ContentKit's own River client in Config.Database.RiverSchema; with WithRiverClient the jobs run on the host's fleet. It runs no DDL; ctx ending stops nothing: Close stops what Start started. Call it once, before serving.

type Error added in v0.83.0

type Error struct {
	Status  int
	Code    string
	Message string
}

Error is a refused call: the route's status and stable code (docs/api/routes.md) and a message for people.

func (*Error) Error added in v0.83.0

func (e *Error) Error() string

type GetCatalogTotalsQuery added in v0.83.0

type GetCatalogTotalsQuery struct {
	// content kinds to total; every kind by default
	Kinds []string
	// languages to break the totals down by; every counted language by default
	Languages []string
}

GetCatalogTotalsQuery is the query of GetCatalogTotals.

type GetCodeQuery added in v0.83.0

type GetCodeQuery struct {
	// the language of the path's prefix and slug
	Lang string
}

GetCodeQuery is the query of GetCode.

type GetCommentBansQuery added in v0.83.0

type GetCommentBansQuery struct {
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetCommentBansQuery is the query of GetCommentBans.

type GetCommentRepliesQuery added in v0.83.0

type GetCommentRepliesQuery struct {
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetCommentRepliesQuery is the query of GetCommentReplies.

type GetCommentsAdminQuery added in v0.83.0

type GetCommentsAdminQuery struct {
	// only comments on this kind
	ContentKind string
	// only comments by this user id
	Author string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetCommentsAdminQuery is the query of GetCommentsAdmin.

type GetCommentsLatestQuery added in v0.83.0

type GetCommentsLatestQuery struct {
	// only this language's comments; every language by default
	Language string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetCommentsLatestQuery is the query of GetCommentsLatest.

type GetContentCanCommentQuery added in v0.83.0

type GetContentCanCommentQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

GetContentCanCommentQuery is the query of GetContentCanComment.

type GetContentCommentsQuery added in v0.83.0

type GetContentCommentsQuery struct {
	// one version of the content item; the item itself by default
	Version string
	// only this language's comments; every language by default
	Language string
	// likes (most liked), best (Wilson lower bound) or newest (the default)
	Sort string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetContentCommentsQuery is the query of GetContentComments.

type GetContentFavoriteQuery added in v0.83.0

type GetContentFavoriteQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

GetContentFavoriteQuery is the query of GetContentFavorite.

type GetContentGroupsQuery added in v0.83.0

type GetContentGroupsQuery struct {
	// the language names, text and slugs resolve in, counts are scoped to and paths are prefixed with
	Language string
}

GetContentGroupsQuery is the query of GetContentGroups.

type GetContentQuery added in v0.83.0

type GetContentQuery struct {
	// the language to show text in; the site's first language, then any, when it has none
	Language string
}

GetContentQuery is the query of GetContent.

type GetContentReactionQuery added in v0.83.0

type GetContentReactionQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

GetContentReactionQuery is the query of GetContentReaction.

type GetContentSimilarQuery added in v0.83.0

type GetContentSimilarQuery struct {
	// only this content kind
	Kind string
	// group ids every listed version is in; a position sort orders by the first
	Groups []string
	// group ids no listed version is in
	ExcludeGroups []string
	// owner group ids (channels): only content one of them owns
	OwnerGroup []string
	// user ids: only content one of them is credited on
	Creators []string
	// public (what anyone opens now), tier, group, ppv or group_ppv (what stays locked at that level): only content at one of them
	Access []string
	// configured tiers: only content locked behind one of them
	Tiers []string
	// version traits every listed version has
	Traits []string
	// name:value of one of the kind's filterable attributes; repeat for more
	Attribute []string
	// published on this day (YYYY-MM-DD) or month (YYYY-MM)
	ReleasedOn string
	// the IANA time zone of released_on and release dates; UTC by default
	Tz string
	// the signed-in reader's unseen, in_progress or completed contents
	ViewStatus string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// 12 by default, at most 50
	Limit int
}

GetContentSimilarQuery is the query of GetContentSimilar.

type GetContentVersionQuery added in v0.83.0

type GetContentVersionQuery struct {
	// the language to show text in; the site's first language, then any, when it has none
	Language string
}

GetContentVersionQuery is the query of GetContentVersion.

type GetContentsQuery added in v0.83.0

type GetContentsQuery struct {
	// only this content kind
	Kind string
	// group ids every listed version is in; a position sort orders by the first
	Groups []string
	// group ids no listed version is in
	ExcludeGroups []string
	// owner group ids (channels): only content one of them owns
	OwnerGroup []string
	// user ids: only content one of them is credited on
	Creators []string
	// public (what anyone opens now), tier, group, ppv or group_ppv (what stays locked at that level): only content at one of them
	Access []string
	// configured tiers: only content locked behind one of them
	Tiers []string
	// version traits every listed version has
	Traits []string
	// name:value of one of the kind's filterable attributes; repeat for more
	Attribute []string
	// published on this day (YYYY-MM-DD) or month (YYYY-MM)
	ReleasedOn string
	// the IANA time zone of released_on and release dates; UTC by default
	Tz string
	// the signed-in reader's unseen, in_progress or completed contents
	ViewStatus string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// newest, oldest, title, popular[:period], top_rated[:period], most_favorited, most_viewed (all time), position or random (periods: 7d, 30d, 90d, 365d, all); the kind's default, else newest
	Sort string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// the previous page's next_cursor
	Cursor string
	// page size: 24 by default, at most 100
	Limit int
}

GetContentsQuery is the query of GetContents.

type GetGlobalCommentBansQuery added in v0.83.0

type GetGlobalCommentBansQuery struct {
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetGlobalCommentBansQuery is the query of GetGlobalCommentBans.

type GetGroupCommentBansQuery added in v0.83.0

type GetGroupCommentBansQuery struct {
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetGroupCommentBansQuery is the query of GetGroupCommentBans.

type GetGroupCommentsQuery added in v0.83.0

type GetGroupCommentsQuery struct {
	// only this language's comments; every language by default
	Language string
	// likes (most liked), best (Wilson lower bound) or newest (the default)
	Sort string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetGroupCommentsQuery is the query of GetGroupComments.

type GetGroupHeldCommentsQuery added in v0.83.0

type GetGroupHeldCommentsQuery struct {
	// the previous page's next
	Cursor string
	// page size: 20 by default, at most 100
	Limit int
}

GetGroupHeldCommentsQuery is the query of GetGroupHeldComments.

type GetGroupMembersQuery added in v0.83.0

type GetGroupMembersQuery struct {
	// the previous page's next_cursor
	Cursor string
	// page size: 50 by default, at most 500
	Limit int
}

GetGroupMembersQuery is the query of GetGroupMembers.

type GetGroupQuery added in v0.83.0

type GetGroupQuery struct {
	// the language names, text and slugs resolve in, counts are scoped to and paths are prefixed with
	Language string
}

GetGroupQuery is the query of GetGroup.

type GetGroupsQuery added in v0.83.0

type GetGroupsQuery struct {
	// only groups of this kind
	Kind string
	// only children of this group
	Parent string
	// only groups scoped by this group
	Scope string
	// only the group with this slug
	Slug string
	// only sort names starting with this letter (a-z), or with anything else (#)
	Letter string
	// the language names, text and slugs resolve in, counts are scoped to and paths are prefixed with
	Language string
	// count contents of this kind; every kind by default
	ContentKind string
	// name (the default), count or newest
	Sort string
	// the previous page's next_cursor
	Cursor string
	// page size: 50 by default, at most 200
	Limit int
}

GetGroupsQuery is the query of GetGroups.

type GetManageContentsQuery added in v0.83.0

type GetManageContentsQuery struct {
	// only this kind
	Kind string
	// draft, scheduled, published (live) or deleted; every state but deleted by default
	State string
	// only this owner group's, which the caller edits
	OwnerGroup string
	// only items whose title, description or body contains this text, ignoring case
	Q string
	// the language to show text in; the site's first language, then any, when it has none
	Language string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetManageContentsQuery is the query of GetManageContents.

type GetMeContentFiltersQuery added in v0.83.0

type GetMeContentFiltersQuery struct {
	// the language of labels and group names
	Language string
}

GetMeContentFiltersQuery is the query of GetMeContentFilters.

type GetMeFavoritesQuery added in v0.83.0

type GetMeFavoritesQuery struct {
	// contents (the default) or groups
	Target string
	// only this content kind or group kind
	Kind string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// the previous page's next_cursor
	Cursor string
	// page size: 24 by default, at most 100
	Limit int
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
}

GetMeFavoritesQuery is the query of GetMeFavorites.

type GetMeGroupsQuery added in v0.83.0

type GetMeGroupsQuery struct {
	// only groups of this kind
	Kind string
	// the language names, text and slugs resolve in, counts are scoped to and paths are prefixed with
	Language string
	// name (the default), count or newest
	Sort string
	// the previous page's next_cursor
	Cursor string
	// page size: 50 by default, at most 200
	Limit int
}

GetMeGroupsQuery is the query of GetMeGroups.

type GetMeHistoryQuery added in v0.83.0

type GetMeHistoryQuery struct {
	// only this content kind
	Kind string
	// seen, in_progress or completed; any by default
	Status string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// the previous page's next_cursor
	Cursor string
	// page size: 24 by default, at most 100
	Limit int
}

GetMeHistoryQuery is the query of GetMeHistory.

type GetMeLibraryQuery added in v0.83.0

type GetMeLibraryQuery struct {
	// the language of the titles
	Language string
	// the previous page's next_cursor
	Cursor string
	// page size: 20 by default, at most 100
	Limit int
}

GetMeLibraryQuery is the query of GetMeLibrary.

type GetMeReactionsQuery added in v0.83.0

type GetMeReactionsQuery struct {
	// like or dislike; both by default
	Value string
	// contents (the default) or groups
	Target string
	// only this content kind or group kind
	Kind string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// the previous page's next_cursor
	Cursor string
	// page size: 24 by default, at most 100
	Limit int
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
}

GetMeReactionsQuery is the query of GetMeReactions.

type GetMeSearchesQuery added in v0.83.0

type GetMeSearchesQuery struct {
	// a signed-out reader's session, which keeps its recent searches
	Session string
	// 10 by default, at most 100
	Limit int
}

GetMeSearchesQuery is the query of GetMeSearches.

type GetMediaHlsQuery added in v0.83.0

type GetMediaHlsQuery struct {
	// audio languages the master playlist keeps; empty keeps none
	Audio []string
	// subtitle languages the master playlist keeps; empty keeps none
	Subs []string
}

GetMediaHlsQuery is the query of GetMediaHls.

type GetMediaQuery added in v0.83.0

type GetMediaQuery struct {
	// only files under this path prefix, e.g. low-res/
	Prefix string
	// files to skip before the URLs start
	Offset int
	// files that get URLs
	Limit int
	// URLs carry their download names
	Download bool
	// editors: uploads with edits, failures, progress and editor views
	Editor bool
}

GetMediaQuery is the query of GetMedia.

type GetMediaUploadFrameQuery added in v0.83.0

type GetMediaUploadFrameQuery struct {
	// the item's kind
	Kind string
	// the item's id
	Id string
	// a Versions kind's version id; absent for its content-level item
	Version string
	// the video upload's path
	Path string
	// seconds into the video
	T float64
	// the still's width in pixels
	W int
}

GetMediaUploadFrameQuery is the query of GetMediaUploadFrame.

type GetMediaVersionHlsQuery added in v0.83.0

type GetMediaVersionHlsQuery struct {
	// audio languages the master playlist keeps; empty keeps none
	Audio []string
	// subtitle languages the master playlist keeps; empty keeps none
	Subs []string
}

GetMediaVersionHlsQuery is the query of GetMediaVersionHls.

type GetMediaVersionQuery added in v0.83.0

type GetMediaVersionQuery struct {
	// only files under this path prefix, e.g. low-res/
	Prefix string
	// files to skip before the URLs start
	Offset int
	// files that get URLs
	Limit int
	// URLs carry their download names
	Download bool
	// editors: uploads with edits, failures, progress and editor views
	Editor bool
}

GetMediaVersionQuery is the query of GetMediaVersion.

type GetModerationHeldQuery added in v0.83.0

type GetModerationHeldQuery struct {
	// comment or content
	Kind string
	// the previous page's next
	Cursor string
	// page size: 20 by default, at most 100
	Limit int
}

GetModerationHeldQuery is the query of GetModerationHeld.

type GetNotificationsInboxQuery added in v0.83.0

type GetNotificationsInboxQuery struct {
	// page size: 20 by default, at most 100
	Limit int
	// the previous page's next_cursor
	Cursor string
	// only unread notices
	Unread *bool
	// archived receipts instead of the active inbox
	Dismissed *bool
}

GetNotificationsInboxQuery is the query of GetNotificationsInbox.

type GetOffersQuery added in v0.83.0

type GetOffersQuery struct {
	// active or withdrawn
	State string
	// only offers OpenRails does not match yet
	Unsynced *bool
	// the previous page's next_cursor
	Cursor string
	// page size: 50 by default, at most 100
	Limit int
}

GetOffersQuery is the query of GetOffers.

type GetPollsAdminQuery added in v0.83.0

type GetPollsAdminQuery struct {
	// only polls in this language
	Language string
	// only polls live in this month, YYYY-MM
	Month string
	// only polls live on this day, YYYY-MM-DD
	Date string
	// only live, scheduled, closed or inactive polls
	State string
	// only polls whose question contains this, ignoring case
	Q string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetPollsAdminQuery is the query of GetPollsAdmin.

type GetPollsQuery added in v0.83.0

type GetPollsQuery struct {
	// only polls in this language
	Language string
	// only polls live in this month, YYYY-MM
	Month string
	// only polls live on this day, YYYY-MM-DD
	Date string
	// page size: 20 by default, at most 100
	Limit int
	// items to skip
	Offset int
}

GetPollsQuery is the query of GetPolls.

type GetRecommendationsQuery added in v0.83.0

type GetRecommendationsQuery struct {
	// only this content kind
	Kind string
	// group ids every listed version is in; a position sort orders by the first
	Groups []string
	// group ids no listed version is in
	ExcludeGroups []string
	// owner group ids (channels): only content one of them owns
	OwnerGroup []string
	// user ids: only content one of them is credited on
	Creators []string
	// public (what anyone opens now), tier, group, ppv or group_ppv (what stays locked at that level): only content at one of them
	Access []string
	// configured tiers: only content locked behind one of them
	Tiers []string
	// version traits every listed version has
	Traits []string
	// name:value of one of the kind's filterable attributes; repeat for more
	Attribute []string
	// published on this day (YYYY-MM-DD) or month (YYYY-MM)
	ReleasedOn string
	// the IANA time zone of released_on and release dates; UTC by default
	Tz string
	// the signed-in reader's unseen, in_progress or completed contents
	ViewStatus string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// 12 by default, at most 50
	Limit int
}

GetRecommendationsQuery is the query of GetRecommendations.

type GetReleaseDatesQuery added in v0.83.0

type GetReleaseDatesQuery struct {
	// month (the default) or day
	By string
	// only this content kind
	Kind string
	// group ids every listed version is in; a position sort orders by the first
	Groups []string
	// group ids no listed version is in
	ExcludeGroups []string
	// owner group ids (channels): only content one of them owns
	OwnerGroup []string
	// user ids: only content one of them is credited on
	Creators []string
	// public (what anyone opens now), tier, group, ppv or group_ppv (what stays locked at that level): only content at one of them
	Access []string
	// configured tiers: only content locked behind one of them
	Tiers []string
	// version traits every listed version has
	Traits []string
	// name:value of one of the kind's filterable attributes; repeat for more
	Attribute []string
	// published on this day (YYYY-MM-DD) or month (YYYY-MM)
	ReleasedOn string
	// the IANA time zone of released_on and release dates; UTC by default
	Tz string
	// the signed-in reader's unseen, in_progress or completed contents
	ViewStatus string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// the previous page's next_cursor
	Cursor string
	// 60 by default, at most 1000
	Limit int
}

GetReleaseDatesQuery is the query of GetReleaseDates.

type GetSearchQuery added in v0.83.0

type GetSearchQuery struct {
	// the query
	Q string
	// only this content kind
	Kind string
	// group ids every listed version is in; a position sort orders by the first
	Groups []string
	// group ids no listed version is in
	ExcludeGroups []string
	// owner group ids (channels): only content one of them owns
	OwnerGroup []string
	// user ids: only content one of them is credited on
	Creators []string
	// public (what anyone opens now), tier, group, ppv or group_ppv (what stays locked at that level): only content at one of them
	Access []string
	// configured tiers: only content locked behind one of them
	Tiers []string
	// version traits every listed version has
	Traits []string
	// name:value of one of the kind's filterable attributes; repeat for more
	Attribute []string
	// published on this day (YYYY-MM-DD) or month (YYYY-MM)
	ReleasedOn string
	// the IANA time zone of released_on and release dates; UTC by default
	Tz string
	// the signed-in reader's unseen, in_progress or completed contents
	ViewStatus string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// relevance (the default) or a list sort of the best matches
	Sort string
	// the group kinds cards name; every kind with a page by default
	CardGroups []string
	// false ranks a signed-in reader's results as anyone's
	Personalize *bool
	// a signed-out reader's session, which keeps its recent searches
	Session string
	// the previous page's next_cursor
	Cursor string
	// page size: 24 by default, at most 100
	Limit int
}

GetSearchQuery is the query of GetSearch.

type GetSitemapContentsQuery added in v0.85.0

type GetSitemapContentsQuery struct {
	// the content kind; required
	Kind string
	// the previous page's next_cursor
	Cursor string
	// page size: 1000 by default, at most 10000
	Limit int
}

GetSitemapContentsQuery is the query of GetSitemapContents.

type GetSitemapGroupsQuery added in v0.85.0

type GetSitemapGroupsQuery struct {
	// the group kind; required
	Kind string
	// the previous page's next_cursor
	Cursor string
	// page size: 1000 by default, at most 10000
	Limit int
}

GetSitemapGroupsQuery is the query of GetSitemapGroups.

type GetStatsActivityQuery added in v0.85.0

type GetStatsActivityQuery struct {
	// the content kind; required
	Kind string
	// the last days, today included: 7 by default, at most 90
	Days int
}

GetStatsActivityQuery is the query of GetStatsActivity.

type GetStatsTopQuery added in v0.85.0

type GetStatsTopQuery struct {
	// the content kind; required
	Kind string
	// views (the default) or comments
	Metric string
	// 7d, 30d (the default), 90d, 365d or all
	Period string
	// public, tier, group, ppv or group_ppv, as lists filter
	Access []string
	// configured tiers, as lists filter
	Tiers []string
	// the language of titles and paths; the site's first by default
	Language string
	// 10 by default, at most 100
	Limit int
}

GetStatsTopQuery is the query of GetStatsTop.

type GetTypeaheadQuery added in v0.83.0

type GetTypeaheadQuery struct {
	// the partial query
	Q string
	// only contents of this kind
	Kind string
	// the group kinds suggested; every kind with a page by default
	GroupKinds []string
	// only content with a live version in this language
	Language string
	// the language of titles, group names and paths, and the preferred language of each card's version; language by default
	DisplayLanguage string
	// lock (the default: everything listed, locked cards marked) or hide (only what the reader opens)
	Mode string
	// per section: 8 by default, at most 20
	Limit int
}

GetTypeaheadQuery is the query of GetTypeahead.

type InteractionsConfig added in v0.83.0

type InteractionsConfig struct {
	// Anonymous is what signed-out visitors may do; zero lets them only read.
	Anonymous content.Anonymous `yaml:"anonymous" json:"anonymous"`
	// CommentMaxLength is the longest comment in characters; 0 is the default.
	CommentMaxLength int `yaml:"comment_max_length" json:"comment_max_length"`
	// Limits are the per-actor rate limits, shared through Deps.Redis.
	Limits LimitsConfig `yaml:"limits" json:"limits"`
	// ModeratorTimeout bounds one moderation call (default 5s);
	// ModeratorCooldown is how long writes are held after it failed
	// repeatedly (default 30s).
	ModeratorTimeout  time.Duration `yaml:"moderator_timeout" json:"moderator_timeout"`
	ModeratorCooldown time.Duration `yaml:"moderator_cooldown" json:"moderator_cooldown"`
}

InteractionsConfig configures the content module.

type IssuanceConfig added in v0.83.0

type IssuanceConfig struct {
	PerHour          int  `yaml:"per_hour" json:"per_hour"`
	AnonymousPerHour int  `yaml:"anonymous_per_hour" json:"anonymous_per_hour"`
	Disabled         bool `yaml:"disabled" json:"disabled"`
}

IssuanceConfig is media.Issuance as data.

type JobsConfig added in v0.83.0

type JobsConfig struct {
	// MaxWorkers is the queue's concurrency; 0 is 2.
	MaxWorkers int `yaml:"max_workers" json:"max_workers"`
}

JobsConfig sizes the engine's River queue (QueueContentKit).

type LimitsConfig added in v0.83.0

type LimitsConfig struct {
	Disabled        bool   `yaml:"disabled" json:"disabled"`
	Comment         []Rate `yaml:"comment" json:"comment"`
	CommentReaction []Rate `yaml:"comment_reaction" json:"comment_reaction"`
	Reaction        []Rate `yaml:"reaction" json:"reaction"`
	Favorite        []Rate `yaml:"favorite" json:"favorite"`
	PollVote        []Rate `yaml:"poll_vote" json:"poll_vote"`
}

LimitsConfig is content.Limits as data: nil rates take the defaults.

type MediaConfig added in v0.83.0

type MediaConfig struct {
	media.Config `yaml:",inline"`
	// WorkerSchema holds the media worker's queue (cmd/media-worker's
	// MEDIA_WORKER_SCHEMA); empty is Database.Schema plus "_media_worker".
	WorkerSchema string         `yaml:"worker_schema" json:"worker_schema"`
	Delivery     DeliveryConfig `yaml:"delivery" json:"delivery"`
	// ProcessOnUpload has the SDK commit each upload as soon as it lands.
	ProcessOnUpload bool `yaml:"process_on_upload" json:"process_on_upload"`
	// CommitLimit limits each uploader's commits; ReadLimit each viewer's
	// reads. Zero takes media's defaults; Deps.Redis shares them.
	CommitLimit RateLimitConfig `yaml:"commit_limit" json:"commit_limit"`
	ReadLimit   RateLimitConfig `yaml:"read_limit" json:"read_limit"`
	// Issuance limits the items each viewer opens per hour.
	Issuance IssuanceConfig `yaml:"issuance" json:"issuance"`
	// Downloads are the version download quotas (anonymous, user, tiers);
	// Deps.Redis shares them.
	Downloads versions.Downloads `yaml:"downloads" json:"downloads"`
}

MediaConfig is the media registry (media.Config: its Namespace is the tenant; content kinds' media are their versions', and each group kind gains cover media, media.ForContent) and how ContentKit serves it.

type MediaFile added in v0.83.0

type MediaFile struct {
	// Path is its upload path ("originals/0001.png"); a named path's {name}
	// is chosen by the server.
	Path string
	Type string
	// Body is read twice: hashed, then sent.
	Body io.ReadSeeker
	// From instead copies this key of the item's bucket, outside
	// ContentKit's namespaces, server-side (root:import:write); Size, when
	// set, is checked against it.
	From string
	Size int64
	Meta map[string]any
}

MediaFile is one file IngestMedia uploads.

type PostCodesAliasesQuery added in v0.83.0

type PostCodesAliasesQuery struct {
	// the language of the paths' prefix and slug
	Lang string
}

PostCodesAliasesQuery is the query of PostCodesAliases.

type PostContentCommentsQuery added in v0.83.0

type PostContentCommentsQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

PostContentCommentsQuery is the query of PostContentComments.

type PostContentDislikeQuery added in v0.83.0

type PostContentDislikeQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

PostContentDislikeQuery is the query of PostContentDislike.

type PostContentFavoriteQuery added in v0.83.0

type PostContentFavoriteQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

PostContentFavoriteQuery is the query of PostContentFavorite.

type PostContentLikeQuery added in v0.83.0

type PostContentLikeQuery struct {
	// one version of the content item; the item itself by default
	Version string
}

PostContentLikeQuery is the query of PostContentLike.

type Probe added in v0.83.0

type Probe struct {
	// Name is stable for dashboards: contentkit_clickhouse,
	// contentkit_media_store, contentkit_redis, contentkit_moderator.
	Name string
	// Check returns nil while the dependency is usable; it honors ctx.
	Check func(context.Context) error
}

Probe checks one optional dependency.

type PutContentGroupsQuery added in v0.83.0

type PutContentGroupsQuery struct {
	// the language names, text and slugs resolve in, counts are scoped to and paths are prefixed with
	Language string
}

PutContentGroupsQuery is the query of PutContentGroups.

type PutMeContentFiltersQuery added in v0.83.0

type PutMeContentFiltersQuery struct {
	// the language of labels and group names
	Language string
}

PutMeContentFiltersQuery is the query of PutMeContentFilters.

type Rate added in v0.83.0

type Rate struct {
	Count int           `yaml:"count" json:"count"`
	Per   time.Duration `yaml:"per" json:"per"`
}

Rate is Count actions per Per.

type RateLimitConfig added in v0.83.0

type RateLimitConfig struct {
	PerSecond float64 `yaml:"per_second" json:"per_second"`
	Burst     int     `yaml:"burst" json:"burst"`
	Disabled  bool    `yaml:"disabled" json:"disabled"`
}

RateLimitConfig is media.RateLimit as data.

type Reply added in v0.83.0

type Reply struct {
	Status int
	Header http.Header
	Body   []byte
}

Reply is a success a generated method leaves undecoded: a stream, or a route answering several body types (Status says which).

func (*Reply) Decode added in v0.83.0

func (r *Reply) Decode(v any) error

Decode decodes the JSON body into v.

type Route added in v0.83.0

type Route struct {
	Method  string
	Path    string
	Handler http.Handler
}

Route is one HTTP registration on the host's root router: the API's subtree. Path ends in "/" and holds every path beneath it; Method is empty for every method. The Handler routes within the selected groups itself.

type RouteGroups added in v0.83.0

type RouteGroups uint

RouteGroups is a set of route groups.

const (
	// ContentRoutes: the content model (/v1/kinds), content items and their
	// versions, catalog totals, config, comments, reactions, favorites, polls,
	// comment bans and moderation (/v1).
	ContentRoutes RouteGroups = 1 << iota
	// UploadRoutes: media uploads and commits (/v1/media/upload).
	UploadRoutes
	// MediaRoutes: media reads and HLS playlists (/v1/media), and versions'
	// reader plans, reader metadata, forks and downloads
	// (/v1/contents/{id}/versions/{vid}/...).
	MediaRoutes
	// CodeRoutes: content-code lookup (/v1/codes).
	CodeRoutes
	// GroupRoutes: group listings, pages and writes, and content's
	// memberships (/v1/groups, /v1/contents/{id}/groups).
	GroupRoutes
	// NotificationRoutes: the user inbox and the host's publish route
	// (/v1/notifications).
	NotificationRoutes
	// OfferRoutes: offers on content, versions and groups, free group joins
	// and the viewer's library (/v1/.../offer, /v1/groups/{id}/join,
	// /v1/me/library, /v1/offers).
	OfferRoutes
	// DiscoveryRoutes: lists, search, typeahead, similar and recommended
	// contents, release dates, the reader's content filters and history,
	// signals and exposures (/v1/contents, /v1/search, /v1/me/history, ...).
	DiscoveryRoutes
	// ImportRoutes: the bulk import a legacy importer writes through
	// (/v1/import).
	ImportRoutes

	// AllRoutes is every route group.
	AllRoutes = ContentRoutes | UploadRoutes | MediaRoutes | CodeRoutes | GroupRoutes | NotificationRoutes | OfferRoutes | DiscoveryRoutes | ImportRoutes
)

func ParseRouteGroups added in v0.83.0

func ParseRouteGroups(names []string) (RouteGroups, error)

ParseRouteGroups reads route group names ("content", "upload", "media", "codes", "groups", "notifications", "offers", "discovery", "import" or "all"), as a server's configuration lists them.

type Routes added in v0.83.0

type Routes struct {
	// Prefix is where the API is mounted: "/api/contentkit" serves
	// /api/contentkit/v1/*. Empty is the root.
	Prefix string
	// Groups are the route groups served. Required.
	Groups RouteGroups
}

Routes selects the HTTP surface: the route groups under Prefix.

type StartOption added in v0.83.0

type StartOption func(*startOptions)

StartOption adjusts Start.

func WithRiverClient added in v0.83.0

func WithRiverClient(fleet *river.Client[pgx.Tx]) StartOption

WithRiverClient runs ContentKit's jobs on the host's River fleet, which riverhelpers.New built with RiverJobs in Config.Database.RiverSchema (beside the host's, AuthKit's and OpenRails' jobs). ContentKit enqueues through it and never starts or stops it.

Directories

Path Synopsis
Package access is ContentKit's access model: the Actor; the key grammar (<tenant>:content:<id>, <tenant>:content:<id>/<version>, <tenant>:group:<id> and configured tiers); rules stored on content, versions and groups; the Gate deciding versions from one billing read per request, its listing Filter and SQL over content_facets; and the Billing dependency.
Package access is ContentKit's access model: the Actor; the key grammar (<tenant>:content:<id>, <tenant>:content:<id>/<version>, <tenant>:group:<id> and configured tiers); rules stored on content, versions and groups; the Gate deciding versions from one billing read per request, its listing Filter and SQL over content_facets; and the Billing dependency.
adapters
authkit module
openrails module
Package catalog is ContentKit's own content: content items (a gallery, a movie, a creator or blog post), their per-language text, creators, versions with their text and segments, the state machine (draft, published, scheduled, deleted), the listing projection (content_facets), the one visibility rule every read applies, the events outbox and the catalog totals.
Package catalog is ContentKit's own content: content items (a gallery, a movie, a creator or blog post), their per-language text, creators, versions with their text and segments, the state machine (draft, published, scheduled, deleted), the listing projection (content_facets), the one visibility rule every read applies, the events outbox and the catalog totals.
cmd
media-gateway command
Command media-gateway is the media gateway (media/gateway).
Command media-gateway is the media gateway (media/gateway).
media-worker command
Command media-worker is the stock media worker (media/worker) for hosts whose registry is plain data: it reads it from a JSON file (media.Registry's JSON).
Command media-worker is the stock media worker (media/worker) for hosts whose registry is plain data: it reads it from a JSON file (media.Registry's JSON).
Package content is ContentKit's interaction module: comments, reactions, favorites and polls over tenant-scoped content references, stored in the host schema's content_* interaction tables.
Package content is ContentKit's interaction module: comments, reactions, favorites and polls over tenant-scoped content references, stored in the host schema's content_* interaction tables.
Package contentref defines the reference vocabulary every ContentKit package and port shares: a tenant-scoped reference to content and the identity of a group.
Package contentref defines the reference vocabulary every ContentKit package and port shares: a tenant-scoped reference to content and the identity of a group.
Package contenturl gives content uniform URLs, [/{lang}]/{route}/{CODE}[/{slug}]: a short random code ContentKit assigns once per content record (unique per tenant across every kind), a decorative slug, and the host's route for the content kind.
Package contenturl gives content uniform URLs, [/{lang}]/{route}/{CODE}[/{slug}]: a short random code ContentKit assigns once per content record (unique per tenant across every kind), a decorative slug, and the host's route for the content kind.
Package discovery is ContentKit's public read surface over its own content: lists, keyword search, typeahead, similar and "for you" lists, the release date index, viewers' content filters and history, and the signals and exposures clients send.
Package discovery is ContentKit's public read surface over its own content: lists, keyword search, typeahead, similar and "for you" lists, the release date index, viewers' content filters and history, and the signals and exposures clients send.
Package eval provides dependency-free golden-query evaluation, aggregate search-quality metrics, versioned reports, baseline comparison, and score-domain-safe threshold sweeps.
Package eval provides dependency-free golden-query evaluation, aggregate search-quality metrics, versioned reports, baseline comparison, and score-domain-safe threshold sweeps.
Package groups is how content is grouped: tags, artists, series, characters, channels, playlists and featured rails.
Package groups is how content is grouped: tags, artists, series, characters, channels, playlists and featured rails.
Package importer is ContentKit's bulk import, POST /v1/import: how a legacy importer writes content, groups, interactions and signals.
Package importer is ContentKit's bulk import, POST /v1/import: how a legacy importer writes content, groups, interactions and signals.
internal
boundaries
Package boundaries holds a test enforcing ContentKit's package layering from the module's dependency graph (go list; no compilation or services).
Package boundaries holds a test enforcing ContentKit's package layering from the module's dependency graph (go list; no compilation or services).
codes
Package codes writes the content-code registry (content_codes) for the packages that own content: contenturl for host content, groups and content for ContentKit's own records.
Package codes writes the content-code registry (content_codes) for the packages that own content: contenturl for host content, groups and content for ContentKit's own records.
contract
Package contract renders ContentKit's route catalog (internal/httpapi) and error-code registry as the files other tools read: api/openapi.json, the Go client's methods (client_gen.go), the browser SDK's generated route table, wire types and error codes, and docs/api/routes.md.
Package contract renders ContentKit's route catalog (internal/httpapi) and error-code registry as the files other tools read: api/openapi.json, the Go client's methods (client_gen.go), the browser SDK's generated route table, wire types and error codes, and docs/api/routes.md.
contract/gen command
Command gen writes the files generated from the route catalog (go generate ./internal/contract).
Command gen writes the files generated from the route catalog (go generate ./internal/contract).
gate
Package gate builds ContentKit's route gates from the host's auth.
Package gate builds ContentKit's route gates from the host's auth.
httpapi
Package httpapi is ContentKit's HTTP surface as data.
Package httpapi is ContentKit's HTTP surface as data.
hub
Package hub is the engine's search and signal planes for one tenant: keyword search and typeahead, and the ClickHouse signal plane.
Package hub is the engine's search and signal planes for one tenant: keyword search and typeahead, and the ClickHouse signal plane.
kindsroute
Package kindsroute serves the site's public content model: the part of kinds.Config clients render kinds, groups and content filters with.
Package kindsroute serves the site's public content model: the part of kinds.Config clients render kinds, groups and content filters with.
normalize
Package normalize cleans user query text before retrieval.
Package normalize cleans user query text before retrieval.
pglock
Package pglock holds Postgres session advisory locks on dedicated connections, so the holder's work may use every connection of its pool.
Package pglock holds Postgres session advisory locks on dedicated connections, so the holder's work may use every connection of its pool.
pgtest
Package pgtest provisions disposable keyword-profile schemas on the CONTENTKIT_TEST_URL Postgres for integration tests.
Package pgtest provisions disposable keyword-profile schemas on the CONTENTKIT_TEST_URL Postgres for integration tests.
ratelimit
Package ratelimit admits events per key against one or more sliding windows ("at most Limit in any Per"), in process or shared through Redis (or Garnet).
Package ratelimit admits events per key against one or more sliding windows ("at most Limit in any Per"), in process or shared through Redis (or Garnet).
resolve
Package resolve is how ContentKit's modules ask whether content exists, is visible, opens and is edited by a viewer.
Package resolve is how ContentKit's modules ask whether content exists, is visible, opens and is edited by a viewer.
signaltest
Package signaltest provisions disposable signal-plane ClickHouse databases for integration tests by applying the real migration lineage.
Package signaltest provisions disposable signal-plane ClickHouse databases for integration tests by applying the real migration lineage.
tcpproxy
Package tcpproxy is a test TCP forwarder that a test takes down (connections refused) and brings back on the same address, to stand in for a provider outage.
Package tcpproxy is a test TCP forwarder that a test takes down (connections refused) and brings back on the same address, to stand in for a provider outage.
testauth
Package testauth is an auth.Authenticator for ContentKit's package tests, which cannot import a real provider: a request's bearer names its actor (As) and an Authorizer answers its Can.
Package testauth is an auth.Authenticator for ContentKit's package tests, which cannot import a real provider: a request's bearer names its actor (As) and an Authorizer answers its Can.
versionid
Package versionid carries a version id between Go, where a content key's is "" for the work itself (contentref.ContentKey), and Postgres, where no version is NULL.
Package versionid carries a version id between Go, where a content key's is "" for the work itself (contentref.ContentKey), and Postgres, where no version is NULL.
worker
Package worker maintains one tenant's keyword documents: it drains the dirty queue and runs a bounded cursor backfill, building every document from ContentKit's own records through its Sources (the catalog's content, the groups').
Package worker maintains one tenant's keyword documents: it drains the dirty queue and runs a bounded cursor backfill, building every document from ContentKit's own records through its Sources (the catalog's content, the groups').
Package kinds is the site's content model as configuration: content kinds, group kinds, tiers, restrictions, site scopes and content filters.
Package kinds is the site's content model as configuration: content kinds, group kinds, tiers, restrictions, site scopes and content filters.
Package media stores app media as a self-describing file system in one private bucket.
Package media stores app media as a self-describing file system in one private bucket.
avatar
Package avatar provides a user-avatar kind for ContentKit media registries.
Package avatar provides a user-avatar kind for ContentKit media registries.
gateway
Package gateway is the media gateway run by cmd/media-gateway.
Package gateway is the media gateway run by cmd/media-gateway.
image
Package image is the media worker's image producer (libvips, CGO): the Image presets' private WebP files, the Zip presets, the public presets' owned generations, editor views, and the kinds' default public images.
Package image is the media worker's image producer (libvips, CGO): the Image presets' private WebP files, the Zip presets, the public presets' owned generations, editor views, and the kinds' default public images.
internal/s3test
Package s3test opens the test bucket from the environment:
Package s3test opens the test bucket from the environment:
internal/videotest
Package videotest builds synthetic videos whose frames identify their time and orientation, and classifies decoded pixels, for poster and preview tests.
Package videotest builds synthetic videos whose frames identify their time and orientation, and classifies decoded pixels, for poster and preview tests.
layout
Package layout defines media object keys, dependency-free so the media gateway can classify paths without importing the media runtime:
Package layout defines media object keys, dependency-free so the media gateway can classify paths without importing the media runtime:
mediatest
Package mediatest opens real media storage for tests outside media/: the S3 test bucket (CONTENTKIT_TEST_S3_*, unset skips) and its Postgres journal and locker (CONTENTKIT_TEST_URL).
Package mediatest opens real media storage for tests outside media/: the S3 test bucket (CONTENTKIT_TEST_S3_*, unset skips) and its Postgres journal and locker (CONTENTKIT_TEST_URL).
s3
Package s3 implements media.Store over aws-sdk-go-v2 for Ceph RGW (production) and MinIO (tests).
Package s3 implements media.Store over aws-sdk-go-v2 for Ceph RGW (production) and MinIO (tests).
token
Package token signs and verifies media access tokens, shared by the host signer and the media gateway so the format cannot drift:
Package token signs and verifies media access tokens, shared by the host signer and the media gateway so the format cannot drift:
versions
Package versions binds media to content versions.
Package versions binds media to content versions.
video
Package video runs an item's video-family presets with ffmpeg: HLS (a byte-range fMP4 ladder with the source's audio and text tracks and a seek sprite), MP4 (a muxed H.264 file at one rung), Audio (an HLS track and an M4A) and Subtitles (clean WebVTT), and grabs the frames of uploads that declare Upload.Frames.
Package video runs an item's video-family presets with ffmpeg: HLS (a byte-range fMP4 ladder with the source's audio and text tracks and a seek sprite), MP4 (a muxed H.264 file at one rung), Audio (an HLS track and an M4A) and Subtitles (clean WebVTT), and grabs the frames of uploads that declare Upload.Frames.
worker
Package worker is the media worker: the one process that places uploads and runs every producer, from the host's worker River schema (Config.Schema) in its database: staged uploads hashed and placed at their blobs (media.Manifests.Place), images, zips, public presets and editor views (media/image, libvips), and HLS, MP4, audio, subtitles and frames (media/video, ffmpeg).
Package worker is the media worker: the one process that places uploads and runs every producer, from the host's worker River schema (Config.Schema) in its database: staged uploads hashed and placed at their blobs (media.Manifests.Place), images, zips, public presets and editor views (media/image, libvips), and HLS, MP4, audio, subtitles and frames (media/video, ffmpeg).
workqueue
Package workqueue is the host's side of the media worker (media/worker): the River schema it drains in the host database, insert-only enqueueing and cancelling of its jobs, and processing progress for the read API.
Package workqueue is the host's side of the media worker (media/worker): the River schema it drains in the host database, insert-only enqueueing and cancelling of its jobs, and processing progress for the read API.
workqueue/metrics
Package metrics exports the host's media worker queue health.
Package metrics exports the host's media worker queue health.
Package migrations owns ContentKit's PostgreSQL and ClickHouse migrations.
Package migrations owns ContentKit's PostgreSQL and ClickHouse migrations.
Package notification adapts the shared helpers/notify inbox to ContentKit's authenticated user API.
Package notification adapts the shared helpers/notify inbox to ContentKit's authenticated user API.
openrails
Package openrails consumes independently acknowledged OpenRails notification events.
Package openrails consumes independently acknowledged OpenRails notification events.
Package offers sells content, versions and groups.
Package offers sells content, versions and groups.
Package popularity ranks host content by a named, bounded policy over the signal plane's canonical window metrics (docs/popularity-policy.md).
Package popularity ranks host content by a named, bounded policy over the signal plane's canonical window metrics (docs/popularity-policy.md).
Package search is ContentKit's keyword retrieval over content_search_documents: exact names and aliases, native-script prefixes and bounded typos for every language, with the visibility join (Eligibility) applied inside every route.
Package search is ContentKit's keyword retrieval over content_search_documents: exact names and aliases, native-script prefixes and bounded typos for every language, with the visibility join (Eligibility) applied inside every route.
Package signal implements ContentKit's signal plane: an append-only stream of host-defined interaction signals plus a durable per-(subject, content) current-state projection, both stored in ClickHouse.
Package signal implements ContentKit's signal plane: an append-only stream of host-defined interaction signals plus a durable per-(subject, content) current-state projection, both stored in ClickHouse.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL