Fork of DavidIlie/seedyn with configurable OIDC issuer (Authentik)
  • TypeScript 90.6%
  • JavaScript 4.5%
  • MDX 3.3%
  • CSS 1.3%
  • Dockerfile 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
andrei-iacobb 6890c24293
Some checks are pending
CI / verify (push) Waiting to run
CI / image (push) Blocked by required conditions
Merge upstream DavidIlie/seedyn: Next.js 16.4.2 RCE fix and ffmpeg.wasm removal
Upstream #12 patches GHSA-2464-fhr4-w9xw with next 16.4.2; #13 replaces
ffmpeg.wasm, whose dynamic worker URL no longer builds under Next 16.4
Turbopack. Fork delta stays the configurable OIDC issuer plus node 24.22.1.
2026-10-26 21:39:27 +00:00
.davidapps feat: add admin and instant clipboard uploads 2026-08-18 18:10:57 +02:00
.deepsec Build Seedyn upload platform 2026-08-18 11:05:01 +02:00
.github/workflows ci: run on GitHub-hosted runners (#14) 2026-10-22 16:11:54 -07:00
content/docs feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
deploy feat: add admin and instant clipboard uploads 2026-08-18 18:10:57 +02:00
packages/cli fix(cli): declare npm provenance metadata 2026-08-31 13:31:34 +03:00
plans feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
prisma Add admin audit log and bounded content table 2026-08-31 14:05:12 +03:00
public Create Seedyn Blue Port identity 2026-08-18 13:54:03 +02:00
scripts feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
src Merge upstream DavidIlie/seedyn: Next.js 16.4.2 RCE fix and ffmpeg.wasm removal 2026-10-26 21:39:27 +00:00
.dockerignore feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
.env.example feat(auth): make the OIDC issuer, label and scope configurable 2026-09-17 23:26:24 +01:00
.gitignore feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
.impeccable.md Create Seedyn Blue Port identity 2026-08-18 13:54:03 +02:00
.npmrc initial commit 2025-10-25 22:10:26 +03:00
.oxlintrc.json feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00
.prettierignore Build Seedyn upload platform 2026-08-18 11:05:01 +02:00
AGENTS.md Build Seedyn upload platform 2026-08-18 11:05:01 +02:00
CLAUDE.md Build Seedyn upload platform 2026-08-18 11:05:01 +02:00
components.json feat: add admin and instant clipboard uploads 2026-08-18 18:10:57 +02:00
Dockerfile Merge upstream DavidIlie/seedyn: Next.js 16.4.2 RCE fix and ffmpeg.wasm removal 2026-10-26 21:39:27 +00:00
next.config.mjs ci: fix the MinIO service and keep only consequential tests (#10) 2026-10-20 15:26:38 -07:00
package.json fix(security): bump next to 16.4.3 for image optimizer RCE (GHSA-2464-fhr4-w9xw) 2026-10-26 21:30:20 +00:00
pnpm-lock.yaml fix(security): bump next to 16.4.3 for image optimizer RCE (GHSA-2464-fhr4-w9xw) 2026-10-26 21:30:20 +00:00
postcss.config.js initial commit 2025-10-25 22:10:26 +03:00
prettier.config.js initial commit 2025-10-25 22:10:26 +03:00
README.md ci: fix the MinIO service and keep only consequential tests (#10) 2026-10-20 15:26:38 -07:00
source.config.ts Build Seedyn upload platform 2026-08-18 11:05:01 +02:00
tsconfig.json feat(gif): convert video with mediabunny and gifenc instead of ffmpeg.wasm (#13) 2026-10-21 10:24:43 -07:00

Seedyn

Seedyn turns a file into a permanent short URL. It is one Next.js 16.3 application with two host roles:

  • seedyn.dave.tips — authenticated library, admin ledger, API keys, and docs.
  • i.dave.tips — anonymous, exact-URL media streaming only.

The same process serves both hosts. A fixed host resolver rejects unknown hosts and rewrites only valid public media paths to the internal streaming route.

The dependency-free CLI lives in packages/cli and is published on npm as seedyn. Run it without installing:

npx seedyn auth login
npx seedyn ./page.html --render-html

Self-hosted installations can persist their own application origin before logging in:

npx seedyn config set api-url https://seedyn.example.com
npx seedyn auth login

Use npx seedyn config get api-url to inspect it. --api-url overrides it for one command, and SEEDYN_API_URL overrides it for the current environment.

Or install the command globally:

npm install --global seedyn
seedyn ./report.pdf

It uploads any file through /api/upload; --render-html is the explicit opt-in for serving verified UTF-8 HTML as a strictly sandboxed page on the separate media origin. The package retains its Postplan MIT attribution in packages/cli/THIRD_PARTY_NOTICES.md.

Local development

Seedyn expects reachable local PostgreSQL and Redis services plus MinIO credentials allowed to create the named development bucket. Copy .env.example to .env.local and provide local values without committing the file. The checked-in commands load .env.local with Next's own precedence rules. For the explicit local-only session provider, set:

APP_URL=http://seedyn.localhost:3000
CDN_URL=http://i.localhost:3000
APP_HOSTS=seedyn.localhost,localhost
MEDIA_HOSTS=i.localhost
SEEDYN_DEV_AUTH=true
[email protected]

Then run:

pnpm install
pnpm storage:setup
pnpm db:migrate
pnpm db:seed
pnpm dev

Open http://seedyn.localhost:3000. The public media origin is http://i.localhost:3000; it intentionally has no index or authenticated UI. The .localhost names resolve to loopback without a hosts-file entry in modern browsers. pnpm dev binds Next to 127.0.0.1; the passwordless local provider refuses to initialize under a manually launched, network-exposed dev server.

The seed and local credentials provider require development mode plus localhost application and PostgreSQL targets. They refuse to adopt an existing federated user with the configured email. NODE_ENV=production pnpm db:seed fails closed. Production uses DavidApps OIDC with an invite-only DavidApps application. The loopback development identity is an explicit admin so local /admin exercises the same authorization path without comparing emails.

DavidApps registration

Seedyn is registered as invite-only OIDC application application_90fb0334-3ca6-4f3d-981a-a60b45f438c0, with public client id N3HqSugmik9n4eSjXybKFybxlyWfKn3K. The non-secret receipt is checked in at .davidapps/auth.public.json. David resolves to the application role admin; production authorization uses that signed role, never an email allowlist.

An approved non-model mover consumed the one-time client secret directly into Seedyn's SOPS-encrypted home-cluster Secret and deleted the owner-only transport artifact after verification. The inactive template under deploy/ remains deliberately empty and is not a deployable plaintext Secret.

Quality gates

pnpm check               # TS7-aware lint and typecheck
pnpm format:check
pnpm test:unit           # upload, S3 gateway, and audit-redaction contracts
pnpm test:direct-upload  # 100 MiB multipart upload against PostgreSQL and MinIO
pnpm build

next is intentionally pinned to exactly 16.3.0. Cache Components, partial prefetching, Server Components, and the Instant Navigation testing API are enabled so framework behavior remains reproducible. Production-build instant testing is exposed only when SEEDYN_BUILD_INSTANT_TESTING=true in controlled CI. The checked-in GitHub Actions workflow runs the same gates against disposable PostgreSQL, Redis, and MinIO services. Pull requests also run pnpm build; pushes to master build and publish the container image instead.

HTTP contracts

  • POST /api/upload — canonical client upload (file, Bearer API key).
  • POST /api/images, /api/files, /api/texts — legacy-compatible aliases.
  • POST /api/uploads — cookie-authenticated browser upload with progress.
  • POST /api/uploads/direct — start a cookie-authenticated multipart session.
  • GET|DELETE /api/uploads/direct/:id — resume status or abort a session.
  • POST /api/uploads/direct/:id/parts — obtain bounded part-ingest URLs.
  • POST /api/uploads/direct/:id/complete — verify and atomically publish.
  • POST /api/uploads/:id/gif — finalize a browser-generated GIF variant.
  • GET|HEAD|OPTIONS https://i.dave.tips/:slug.:ext — public media with conditional requests and single-range support.
  • GET /api/healthz — app-origin/direct-probe liveness; /api/readyz is dependency readiness restricted to loopback or the downward-API POD_IP.

The authenticated product documentation under /docs is the normative user and agent guide. The complete design record and acceptance plan live in plans/.

The admin-only /admin route aggregates Seedyn's own PostgreSQL data: signed-in users, original and GIF storage, active API keys, upload activity, content types, and recent objects. DavidApps does not expose a broad user-directory analytics API, so granted users appear after their first Seedyn sign-in.

Storage and operations

Uploads through 64 MiB stream through bounded mode-0600 files in /tmp/seedyn-uploads, then to the private MinIO bucket. Larger browser files use 16 MiB S3 multipart parts and avoid temporary disk. Database rows publish application-generated slugs only after storage and full server-side integrity verification succeed; failed database writes compensate the object. Run the read-only consistency report and expired-session sweeper with:

pnpm storage:check
pnpm uploads:sweep

Production storage must independently abort incomplete multipart uploads after seven days. See the authenticated operations guide for proxy versus presigned transport and CORS requirements.

The Docker image runs as a non-root user, exposes port 3000, includes Prisma migrations for an init-container command, and supports a read-only root when /tmp and .next/cache are writable mounts.