coop

module
v1.8.16 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT

README

Coop

Self-hosted, parent-curated YouTube for kids.

Children get a native app and a full browser client that look and feel like real YouTube: a home feed, subscriptions, channel pages, search, and a vertically scrolling Shorts feed. Every channel they can watch was approved by a parent first. When they find something new, they ask, and a parent approves it from their own app.

No Google account on the child's device. No comments, in either direction. No livestreams.

Want us to set it up? Nerds Who Fish offers a one-time Coop setup for $500. You provide a computer capable of running Docker, and we'll install and configure Coop for your family. Send us a message through our contact form and mention Coop.

See Coop in action

Watch the Cooper The Cop demo

Watch the full Cooper The Cop demo

Coop keeps the YouTube experience children already understand while moving approval, policy, and recommendation controls onto a parent's device.

Built for kids
Watch and choose what is next Discover safely Search with approval built in
Cooper Watch playing an approved video with Watch Next recommendations Cooper Watch suggesting a new channel that requires parent approval Cooper Watch search results with unapproved videos locked behind an Ask button
Familiar playback, reactions, subscriptions, and Watch Next without comments or livestreams. New channels can still appear, but the child must ask before watching them. Approved content plays normally while everything else stays behind a clear approval boundary.
Controlled by parents
Approve from your phone Set rules per child Tune recommendations
Cooper The Cop showing a child's channel approval request Cooper The Cop showing discovery, search, and device controls for one child Cooper The Cop explaining and tuning a child's recommendation mix
Review requests and approve or deny them without touching the child's device. Give every child their own discovery, search, Shorts, pairing, and content settings. See why videos rank, then adjust the mix by channel instead of trusting a black-box feed.

Status: ready for daily use. The backend and both native iOS apps are complete, deployed, and ready for everyday family use. Production deployment, registered-device installation, backups, recovery, and child-device restrictions are documented in the deployment guide. The reasoning behind Coop's architectural decisions lives in adr/.

How it works

graph TD
  Child[Child app<br/>Cooper Watch] -->|child token| API[Coop backend<br/>Go]
  Browser[Child browser<br/>Cooper Watch web] -->|HttpOnly device session| API
  Parent[Parent app<br/>Cooper The Cop] -->|parent token| API
  API --> DB[(Postgres)]
  API --> YT[YouTube Data API v3<br/>your own key]
  Child -->|nocookie embed| Player[YouTube IFrame player]
  Browser -->|nocookie embed| Player

Each family runs their own backend with their own YouTube Data API key. The backend holds the allowlists, evaluates policy, and caches everything it fetches. Playback uses YouTube's official embedded player, so creators receive real views and there is no stream extraction involved.

Features

  • Channel-level allowlists, global across the family or per child.
  • Three channel states: allowed, requestable (visible, asks first), and blocked (invisible).
  • Request and approve loop. A child asks, a parent approves from their phone.
  • Optional push notifications with your own APNs key: requests reach parents and decisions reach children, and while the child app is open, an approval simply appears without disturbing playback.
  • Negative keywords that block individual videos inside otherwise-allowed channels, with every suppression visible to the parent and overridable in one tap.
  • Multiple children, each with their own subscriptions, allowlist, and settings.
  • Computer access at the Coop hostname, linked by scanning a short-lived QR code from either native app or by entering a parent's one-time pairing code.
  • Multiple parents, scoped to the children they are allowed to manage.
  • Local subscriptions, likes, and dislikes that never touch YouTube.
  • No livestreams, including finished livestream VODs.
  • Child search across channels and videos, with blocked results hidden and requestable videos locked behind approval.
  • Opt-in new-channel discovery, mixed conservatively into Home and Watch Next with explanations and the same approval boundary as search.
  • A shuffled, looping Shorts feed that mounts only the visible player and never includes locked channels.
  • Parent-tunable recommendations over the approved pool, ranked entirely from cached data, with seeded exploration so Home and Watch Next stay fresh between visits.

Components

Path What
cmd/coopd The backend server binary
internal/config Configuration loading, TOML file overlaid by environment
internal/store Postgres models and migrations
internal/policy Allowlist and keyword evaluation. Pure, no I/O
internal/youtube Data API client, response cache, quota budgets
internal/youtubeclient Family-scoped YouTube client construction
internal/ingest Scheduled approved-channel catalog refresh
internal/cleanup Daily expiry and ledger cleanup
internal/rank Recommendation scoring. Pure, no I/O
api/openapi.yaml The API contract, source of truth for both clients
ios/CoopKit Generated Swift API client and shared native code
ios/CooperTheCop SwiftUI parent app and XcodeGen project source
ios/CooperWatch SwiftUI child app and XcodeGen project source
web React and TypeScript Cooper Watch browser client
adr/ Architecture decision records
docs/DEPLOYMENT.md Production setup, device restrictions, recovery, and operations

The parent app (Cooper The Cop) includes setup, policy administration, retained audit history, account deletion, and the recommendation mixer. The native and browser Cooper Watch clients include pairing, feeds, subscriptions, search, approvals, playback, reactions, sharing, and Shorts.

In the native app, locking the screen or switching apps suspends regular-video playback in place so returning keeps the current position. Leaving the watch page or a parent blocking the video still clears the player.

Coop is ready for daily family use through its self-hosted deployment and registered-device release tooling. App Store publication still depends on the legal, content-rights, signing, account, metadata, and human approval gates listed in ios/AppStore/README.md.

Requirements

  • Go 1.26 or newer
  • Node.js 24 or newer for building the embedded browser client
  • Postgres 16 or newer
  • A Google Cloud project with the YouTube Data API v3 enabled, and an API key
  • Xcode 16 or newer and XcodeGen 2.46 or newer for the native apps

Child-device network filtering must allow your Coop backend and the small set of YouTube player hosts listed in the deployment guide.

Every family needs their own API key. The YouTube API Developer Policies forbid embedding API credentials in open source projects, so there is no shared key and there never will be. The upside is that each family gets its own full daily quota.

Development

make help          # list targets
make dev-db        # start a local Postgres in Docker
make migrate       # apply migrations
make run           # run the server
make test          # run tests
make lint          # vet and staticcheck

make test-integration runs the PostgreSQL integration tests against the dev server. Each test creates and drops its own database so migration rollbacks and cache purges cannot affect other tests. For another local test server, set COOP_TEST_DATABASE_DSN and run go test -race -tags=integration ./.... The connection role needs CREATEDB; the dev and CI PostgreSQL roles already have it. Tests do not modify the database named in the DSN, which is used only to create the temporary databases.

The shared Swift package and native apps can be checked independently:

swift test --package-path ios/CoopKit
xcodegen generate --spec ios/CooperTheCop/project.yml
xcodebuild -skipPackagePluginValidation \
  -project ios/CooperTheCop/CooperTheCop.xcodeproj \
  -scheme CooperTheCop -destination 'generic/platform=iOS Simulator' build \
  CODE_SIGNING_ALLOWED=NO
xcodegen generate --spec ios/CooperWatch/project.yml
xcodebuild -skipPackagePluginValidation \
  -project ios/CooperWatch/CooperWatch.xcodeproj \
  -scheme CooperWatch -destination 'generic/platform=iOS Simulator' build \
  CODE_SIGNING_ALLOWED=NO

Registered-device Ad Hoc packages are published to Fledge, which hosts the archives and serves the install pages. Point updates.base_url at that server and Coop reports the published build to clients, so a device needs one server address rather than two. The release workflow uses Quill's optional Apple signing action with the persistent Distribution identity in APPSTORE_CERTIFICATES_FILE_BASE64 and APPSTORE_CERTIFICATES_PASSWORD rather than asking Xcode to create a certificate on each runner. The parent and child applications receive separate certificate-, device-, and capability-bound Ad Hoc profiles through the existing ASC_ISSUER_ID, ASC_KEY_ID, and ASC_KEY_P8 credentials.

Catalog refresh

Catalog refresh saves uploads independently of the channel RSS classification signal. If RSS remains temporarily unavailable after bounded retries, the refresh completes with a catalog classification deferred warning and retries RSS at the next configured channel refresh interval. Existing RSS classifications remain intact; new videos retain the explicit duration-based fallback described in ADR 0002, which can misclassify short regular videos until RSS recovers. Upstream failures remain visible in traces, while permanent feed errors and database write failures still fail the refresh.

Removing a parent

An administrator can remove a parent account while another administrator remains. Removal revokes the account's sessions, authentication challenges, invitations, push registrations, and child scopes. Channel approvals, video blocks, video overrides, request decisions, and audit history remain in place; their references to the removed parent become empty. Children keep the same effective viewing policy. See ADR 0023 for the attribution and rollback trade-offs.

Contributing

Decisions with real trade-offs get an ADR in adr/ before the code lands. Prose in this repo is written one sentence per line, so that editing a sentence produces a one-line diff.

License

MIT. See LICENSE.

Directories

Path Synopsis
cmd
coopd command
Command coopd is the Coop backend server.
Command coopd is the Coop backend server.
internal
api
Package api serves Coop's HTTP surface: a parent side behind a session, and a read-mostly child side behind a paired device token.
Package api serves Coop's HTTP surface: a parent side behind a session, and a read-mostly child side behind a paired device token.
auth
Package auth handles parent credentials, device tokens, pairing codes, and the scoping rules that decide which children a parent may act on.
Package auth handles parent credentials, device tokens, pairing codes, and the scoping rules that decide which children a parent may act on.
cancellation
Package cancellation identifies operations that failed solely because their caller canceled.
Package cancellation identifies operations that failed solely because their caller canceled.
cleanup
Package cleanup removes expired operational data on a daily schedule.
Package cleanup removes expired operational data on a daily schedule.
config
Package config loads Coop's configuration from an optional TOML file overlaid by environment variables, and validates the result.
Package config loads Coop's configuration from an optional TOML file overlaid by environment variables, and validates the result.
crypto
Package crypto seals the secrets Coop stores on a family's behalf: their YouTube API key and any TOTP secrets.
Package crypto seals the secrets Coop stores on a family's behalf: their YouTube API key and any TOTP secrets.
domain
Package domain holds the value types shared between policy evaluation and persistence.
Package domain holds the value types shared between policy evaluation and persistence.
feed
Package feed assembles what a child sees, composing the cached catalog with the policy engine and recording what was filtered.
Package feed assembles what a child sees, composing the cached catalog with the policy engine and recording what was filtered.
ingest
Package ingest keeps the local catalog populated from approved channels.
Package ingest keeps the local catalog populated from approved channels.
policy
Package policy decides what a child may see.
Package policy decides what a child may see.
push
Package push delivers family notifications through APNs: a child's channel request to the parents who can act on it, and the decision back to the child's devices.
Package push delivers family notifications through APNs: a child's channel request to the parents who can act on it, and the decision back to the child's devices.
rank
Package rank orders policy-approved videos from local activity signals.
Package rank orders policy-approved videos from local activity signals.
store
Package store holds Coop's Postgres models and migrations.
Package store holds Coop's Postgres models and migrations.
telemetry
Package telemetry configures Coop's OpenTelemetry runtime.
Package telemetry configures Coop's OpenTelemetry runtime.
version
Package version carries build metadata stamped in by the linker.
Package version carries build metadata stamped in by the linker.
webapp
Package webapp serves the embedded Cooper Watch browser client.
Package webapp serves the embedded Cooper Watch browser client.
youtube
Package youtube talks to the YouTube Data API and the public channel feeds, with a response cache and a daily spend ledger in front of both.
Package youtube talks to the YouTube Data API and the public channel feeds, with a response cache and a daily spend ledger in front of both.
youtubeclient
Package youtubeclient constructs family-scoped YouTube clients.
Package youtubeclient constructs family-scoped YouTube clients.

Jump to

Keyboard shortcuts

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