x,edgraph,worker: add a reserved-namespace plugin registry#9753
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
x.RegisterReservedNamespace: a plugin can claim ownership of a sub-namespace under the reserveddgraph.prefix (a dynamic predicate prefix and/or an exact predicate/type allowlist) so its names can be created at runtime viaAltereven 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.newReservedPredicateGuard) — a write to a value-locked predicate is rejected unless the request context carries the namespace'sTrustMarker(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:
TrustMarkerpresence for value-locked predicates at registration —RegisterReservedNamespacepanics, 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 isparseRequest.Tests
x/reserved_namespace_test.gocovers dynamic-prefix and exact membership, the predicate/type split, value-lock lookup (including case-insensitivity), and the TrustMarker-required panic.