Skip to content

fix: make node slugs unique per user - #84

Merged
Skeyelab merged 1 commit into
masterfrom
fix/per-user-node-slugs
Sep 22, 2026
Merged

Skeyelab merged 1 commit into
masterfrom
fix/per-user-node-slugs

Conversation

@Skeyelab

Copy link
Copy Markdown
Member

Why

Node slugs were globally unique (nodes_slug_key), so two users couldn't both name a node hermes. Slugs are display-only (generated from the node name), so they're now unique per user, matching routes.

Changes

  • db/schema.ts: nodes_slug_key becomes nodes_user_id_slug_key on (user_id, slug).
  • Migration 0003:
    • Creates each per-user index before dropping the global one, so uniqueness is enforced throughout.
    • Uses IF [NOT] EXISTS everywhere, so it's safe on any database where part of it already applied.
    • Also re-asserts the routes change from 0002. That migration's snapshot never recorded its own change (drizzle-kit generate regenerated it), and production had drifted back to the global route index until today, when I fixed it by hand.
  • tests/db/schema-unique-indexes.test.ts pins both unique indexes to (user_id, slug). It was written first: the nodes case failed, and the routes case passed because only production had drifted.

Production

The migrations workflow is disabled, so after merge this is applied by hand: rehearsed on a Neon branch, then applied in one transaction that also records it in drizzle.__drizzle_migrations (file SHA-256 + journal when), as drizzle-kit migrate would.

yarn vitest run: 247/247. Lint clean.

Node slugs were globally unique, so two users couldn't both name a node
"hermes". They're display-only (generated from the name), so scope them per
user like routes.

Migration 0003 also re-asserts the routes change from 0002, whose snapshot
never recorded it (drizzle-kit regenerated it) and which production had
drifted away from until 2026-09-22. Each per-user index is created before the
global one is dropped, and every statement uses IF [NOT] EXISTS, so it's safe
wherever part of it already applied.

A schema test pins both unique indexes to (user_id, slug).
@strix-security

strix-security Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Review summary

Reviewed the schema, migration, journal, generated snapshot, and new test introduced by this PR. The change relaxes node-slug uniqueness from global (slug) to per-user (user_id, slug), matching the existing routes behavior. Tracing slug usage across the codebase confirms it is display-only and never used as an access-control or lookup key: node authentication uses token hashes, node retrieval uses IDs, and webhook dispatch scopes routes by (userId, routeSlug). The migration is idempotent and ordered so uniqueness is preserved at every step. No security issues were identified in the changed code.

Updated for 3c873e6.


Reviewed by Strix
Re-run review · Configure security review settings

@Skeyelab
Skeyelab merged commit e661dcb into master Sep 22, 2026
3 checks passed
@Skeyelab
Skeyelab deleted the fix/per-user-node-slugs branch September 22, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant