Skip to content

x,edgraph,worker: add a reserved-namespace plugin registry#9753

Merged
matthewmcneely merged 3 commits into
mainfrom
oss-reserved-namespace-registry
Jun 18, 2026
Merged

x,edgraph,worker: add a reserved-namespace plugin registry#9753
matthewmcneely merged 3 commits into
mainfrom
oss-reserved-namespace-registry

Conversation

@matthewmcneely

Copy link
Copy Markdown
Contributor

What

Adds x.RegisterReservedNamespace: a plugin can claim ownership of a sub-namespace under the reserved dgraph. prefix (a dynamic predicate prefix and/or an exact predicate/type allowlist) so its names can be created at runtime via Alter even though they aren't part of the pre-defined initial schema, and optionally value-lock a subset of its predicates to a trusted in-process writer.

The three reserved-namespace enforcement points consult the registry:

  • parseSchemaFromAlterOperation — a registered predicate/type passes the reserved-but-not-pre-defined check (IsRegisteredReservedPredicate / IsRegisteredReservedType).
  • worker.proposeAndWait — a registered predicate may be mutated before its schema exists.
  • the mutation value guard (newReservedPredicateGuard) — a write to a value-locked predicate is rejected unless the request context carries the namespace's TrustMarker (ReservedPredicateValueLock).

Why

There is currently no way for a downstream consumer to own names under dgraph. — the prefix is all-or-nothing reserved. This adds a narrow, explicit extension point rather than a broad carve-out.

Safety

With no registration the registry is empty and every check reports false, so a stock build keeps the pristine behavior: nothing under dgraph. may be created except the pre-defined names, and the GraphQL reserved-value path (IsGraphql / IsOtherReservedPredicate) is unchanged.

History

Supersedes #9749, which GitHub auto-closed when its stacked base branch (the AlterNoAuth PR #9748, now merged) was deleted. Both review items from #9749 are already addressed in this branch:

  • case-fold the value-lock lookups on both register and lookup (matching the predicate/type lookups), with a case-variant regression test;
  • enforce TrustMarker presence for value-locked predicates at registration — RegisterReservedNamespace panics, so the misconfiguration trips at startup rather than at mutation time.

ParseMutationObject(ctx, mu) is an exported signature change (was (mu, isGraphql bool)) so the value guard can read the request context. The only in-tree caller is parseRequest.

Tests

x/reserved_namespace_test.go covers dynamic-prefix and exact membership, the predicate/type split, value-lock lookup (including case-insensitivity), and the TrustMarker-required panic.

matthewmcneely and others added 3 commits June 18, 2026 17:43
Introduce x.RegisterReservedNamespace, letting a plugin claim ownership of a sub-namespace under the reserved `dgraph.` prefix so its predicates and types can be created at runtime via Alter even though they are not part of the pre-defined initial schema, and optionally value-lock a subset of those predicates to a trusted in-process writer.

The three reserved-namespace enforcement points now consult the registry: parseSchemaFromAlterOperation allows a registered predicate/type through the reserved-but-not-pre-defined check (IsRegisteredReservedPredicate / IsRegisteredReservedType); worker.proposeAndWait allows a registered predicate to be mutated before its schema exists; and the mutation value guard rejects writes to a value-locked predicate unless the request context carries the namespace's TrustMarker (ReservedPredicateValueLock).

With no registration the registry is empty and every check reports false, so a stock build keeps the pristine behavior: nothing under `dgraph.` may be created except the pre-defined names, and the GraphQL reserved-value path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ReservedNamespace ownership matches names case-insensitively (the predicate, type, and prefix lookups all ToLower), but the value lock stored and looked up names raw. A namespace that value-locked a name with any uppercase letter (e.g. dgraph.foo.Secret) left the lowercased variant dgraph.foo.secret owned but not locked, so a mutation to it skipped the trust-marker check. Every reserved name today is lowercase, so nothing breaks in practice, but the two halves disagreed on case.

Lowercase on both register and lookup, matching the predicate/type loops. Deliberately no ParseAttr in the value-lock path: the mutation guard passes the bare predicate (no namespace separator), so ParseAttr would panic — which is why it mirrors IsOtherReservedPredicate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RegisterReservedNamespace documented that TrustMarker is required when ValueLocked is non-empty but never checked it. A caller that set ValueLocked with a nil TrustMarker would make the predicate unwritable by everyone, including its owner: the guard's marker != nil test stays false, so the write is always rejected. It fails closed (safe), but surfaces only at mutation time, far from the registration that caused it.

Registration runs in init(), so panic on the misconfiguration to catch it at startup instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@matthewmcneely matthewmcneely requested a review from a team as a code owner June 18, 2026 21:45
@matthewmcneely matthewmcneely merged commit 71f9dae into main Jun 18, 2026
18 checks passed
@matthewmcneely matthewmcneely deleted the oss-reserved-namespace-registry branch June 18, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant