ArticleReadMain page

Nathan's Technology Wiki / Projects

OpenLinks

A self-hosted link-profile platform with identity, analytics, moderation, and object storage.

Purpose and status

OpenLinks is a self-hosted profile and link-publishing application. A creator can maintain a public profile, social identities, ordered links, themes, and basic analytics while keeping application data on personally controlled infrastructure.

Application version0.1.0
Runtime stateApp, database, and object storage healthy
LicenseAGPL-3.0-or-later

Capabilities

Profile publishing

  • Reserved and constrained usernames protect system routes and namespace integrity.
  • Profiles include display identity, biography, theme configuration, social links, and ordered/pinned links.
  • Drag-and-drop packages support deliberate link ordering and profile management.

Platform controls

  • Invite-only operation can limit account creation.
  • Moderation actions are represented in the database rather than hidden in logs.
  • Event and daily analytics separate raw activity from summarized reporting.

Architecture and request flow

BrowserNext.js renders profiles and protected management experiences.
ApplicationAuth.js, Zod, React Hook Form, Zustand, and server code manage identity, validation, and state.
PostgreSQLPrisma stores users, profiles, links, sessions, analytics, and moderation records.
MinIOS3-compatible storage provides the object layer for uploads and presigned access.
NPMThe public hostname reaches the application while PostgreSQL stays on the private Compose network.

The production stack runs three long-lived services: the Next.js app, PostgreSQL 17, and MinIO. A separate initialization profile creates storage resources only when deliberately requested.

Data model and migrations

The recorded Prisma schema includes User, Account, Session, VerificationToken, Profile, SocialLink, Link, AnalyticsEvent, AnalyticsDaily, ReservedUsername, and ModerationAction models. Four production migrations document the initial schema, username constraints, removal of unused theme enums, and moderation support.

Migration lesson
Identity and public URL constraints belong in versioned database migrations, not only in interface validation.

Identity, secrets, and storage security

  • Auth.js uses the Prisma adapter, with optional GitHub and Google providers.
  • Password hashing uses bcrypt cost 12 for credentials managed by the application.
  • Production seeding intentionally skips demo accounts and sample content.
  • The application trusts the reverse-proxy host only through deliberate production configuration.
  • Database, authentication, OAuth, analytics-salt, and S3 credentials are runtime configuration and are not documented in the wiki.
  • Presigned object URLs must use a browser-reachable hostname while storage credentials remain internal.
August 2026 authentication review and credential rotation
The public Auth.js flow was verified to use HttpOnly, Secure, SameSite cookies and no-store responses, and privileged operations recheck current database state. On August 3, the sole administrator password was rotated to a random 32-character value after a validated database backup. The new bcrypt cost-12 hash and the real public sign-in flow were verified; the plaintext remains only in the protected local master credential file. The authenticated Auth.js session reports the expected administrator role, but the protected /dashboard route currently returns 404 after login and requires a separate routing fix. Existing Auth.js JWT sessions are not individually revoked by a password change, so shorter/revocable privileged sessions remain important follow-up work. Other remaining work includes stronger enrollment policy, durable login throttling, credential-email verification, recovery, and MFA/passkeys. Read the complete login-security review.

Operations, backup, and recovery

The application is published on a dedicated host port behind NPM. PostgreSQL is not published to the host. Named volumes preserve database and MinIO data, and the deployment directory contains timestamped database backups plus a restoration script.

Preflight

  • Generate Prisma client, type-check, build, and validate Compose.
  • Confirm database health before application startup.
  • Verify public URL and S3 signing configuration agree.
  • Draft the OpenLinks wiki changes for the planned version, schema, identity, storage, and operating-model update.

Postflight

  • Test sign-in, profile rendering, link ordering, uploads, and access denial.
  • Confirm app, PostgreSQL, and MinIO health.
  • Restore a backup into an isolated test before calling recovery complete.
  • Update the OpenLinks wiki with the verified release, migrations, limitations, recovery evidence, and lessons.

Lessons and roadmap

  • Public usernames are security-sensitive routing data and need reserved-name protection.
  • Authentication behind a reverse proxy requires trusted-host configuration and accurate public origins.
  • Object storage introduces two endpoints: the private service endpoint and the safe public signing origin.
  • Analytics should minimize retained personal data and use protected salts.
  • Future work should add documented release tags, automated tests, upload lifecycle checks, and scheduled restoration drills.