Pure.Authorization.OpenFGA.Model.HashCodes
0.1.0-preview.0.1.0
dotnet add package Pure.Authorization.OpenFGA.Model.HashCodes --version 0.1.0-preview.0.1.0
NuGet\Install-Package Pure.Authorization.OpenFGA.Model.HashCodes -Version 0.1.0-preview.0.1.0
<PackageReference Include="Pure.Authorization.OpenFGA.Model.HashCodes" Version="0.1.0-preview.0.1.0" />
<PackageVersion Include="Pure.Authorization.OpenFGA.Model.HashCodes" Version="0.1.0-preview.0.1.0" />
<PackageReference Include="Pure.Authorization.OpenFGA.Model.HashCodes" />
paket add Pure.Authorization.OpenFGA.Model.HashCodes --version 0.1.0-preview.0.1.0
#r "nuget: Pure.Authorization.OpenFGA.Model.HashCodes, 0.1.0-preview.0.1.0"
#:package Pure.Authorization.OpenFGA.Model.HashCodes@0.1.0-preview.0.1.0
#addin nuget:?package=Pure.Authorization.OpenFGA.Model.HashCodes&version=0.1.0-preview.0.1.0&prerelease
#tool nuget:?package=Pure.Authorization.OpenFGA.Model.HashCodes&version=0.1.0-preview.0.1.0&prerelease
Pure.Authorization.OpenFGA.Model.HashCodes
Deterministic hash code implementations for OpenFGA authorization model abstractions in the Pure ecosystem.
Status: early development. The API below is implemented, but no package has been published yet.
Overview
Pure.Authorization.OpenFGA.Model.HashCodes provides deterministic, byte-enumerable hash codes for every type in the Pure.Authorization.OpenFGA.Model.Abstractions layer. Following the approach of Pure.Chart.Model.HashCodes, each type wraps one abstraction interface and produces a stable byte sequence by prepending a fixed 16-byte type-discriminator prefix before hashing the object's fields with Pure.HashCodes.DeterminedHash. The resulting sequences enable structural equality checks and content-addressed storage of OpenFGA authorization models and relationship tuples.
API
All types are sealed record and implement IDeterminedHash (which extends IEnumerable<byte>). Hashes are computed lazily, when the bytes are enumerated. The namespaces mirror the folders of the abstractions: a hash of an interface from Pure.Authorization.OpenFGA.Model.Abstractions.Rewrite lives in Pure.Authorization.OpenFGA.Model.HashCodes.Rewrite, and so on.
One hash per interface
Each of these hashes has its own 16-byte type prefix, so two values of different types never collide even when their fields match.
| Namespace | Type | Wraps | Hashed fields |
|---|---|---|---|
Model |
AuthorizationModelHash |
IAuthorizationModel |
SchemaVersion, Types (set), Conditions (set) |
Model |
TypeDefinitionHash |
ITypeDefinition |
Name, Relations (set) |
Model |
RelationDefinitionHash |
IRelationDefinition |
Name, Rewrite |
Rewrite |
DirectHash |
IDirect |
Forms (set) |
Rewrite |
ComputedRelationHash |
IComputedRelation |
Relation |
Rewrite |
RelationFromHash |
IRelationFrom |
Relation, Tupleset |
Rewrite |
UnionHash |
IUnion |
Children (set) |
Rewrite |
IntersectionHash |
IIntersection |
Children (set) |
Rewrite |
DifferenceHash |
IDifference |
Base, Subtract (ordered) |
SubjectForm |
TypeFormHash |
ITypeForm |
Type |
SubjectForm |
SubjectSetFormHash |
ISubjectSetForm |
Type, Relation |
SubjectForm |
WildcardFormHash |
IWildcardForm |
Type |
SubjectForm |
ConditionalFormHash |
IConditionalForm |
Form, Condition |
Condition |
ConditionHash |
ICondition |
Name, Expression, Parameters (set) |
Condition |
ConditionParameterHash |
IConditionParameter |
Name, Type |
Condition |
ConditionParameterTypeHash |
IConditionParameterType |
Name, GenericTypes (ordered) |
Object |
ObjectHash |
IObject |
Type, Id |
Subject |
SubjectSetHash |
ISubjectSet |
Object, Relation |
Subject |
TypedWildcardHash |
ITypedWildcard |
Type |
Tuple |
RelationshipConditionHash |
IRelationshipCondition |
Name, Context (canonical JSON) |
Tuple |
ConditionalTupleHash |
IConditionalTuple |
Subject, Relation, Object, Condition |
Dispatching hashes
IRewrite, ISubjectForm and ISubject are closed sets of variants, and ITuple may or may not carry a condition. Their hashes pick the hash of the concrete variant and yield exactly the same bytes, so new SubjectHash(obj) equals new ObjectHash(obj).
| Namespace | Type | Wraps | Dispatches to |
|---|---|---|---|
Rewrite |
RewriteHash |
IRewrite |
DirectHash, ComputedRelationHash, RelationFromHash, UnionHash, IntersectionHash, DifferenceHash |
SubjectForm |
SubjectFormHash |
ISubjectForm |
TypeFormHash, SubjectSetFormHash, WildcardFormHash, ConditionalFormHash |
Subject |
SubjectHash |
ISubject |
ObjectHash, SubjectSetHash, TypedWildcardHash |
Tuple |
TupleHash |
ITuple |
ConditionalTupleHash for an IConditionalTuple. A plain tuple hashes its Subject, Relation and Object under the TupleHash prefix |
Enumerating RewriteHash, SubjectFormHash or SubjectHash over an implementation outside its closed set throws ArgumentException. This also holds when the unknown variant is nested anywhere inside a larger hash, including inside a set.
Hashing rules
Collection order
Collections that OpenFGA treats as sets are hashed order-insensitively. Each element is hashed once, then the element hashes are combined by Pure.HashCodes' sorting aggregate (new DeterminedHash(IEnumerable<IDeterminedHash>)):
IAuthorizationModel.TypesandIAuthorizationModel.ConditionsITypeDefinition.RelationsIDirect.FormsIUnion.ChildrenandIIntersection.ChildrenICondition.Parameters
Duplicates are not collapsed: [a, a] and [a] hash differently.
Order is significant where it carries meaning:
IDifference:a but not bandb but not ahash differently. Base and Subtract are hashed in that order.IConditionParameterType.GenericTypes: type arguments keep their positions. The element hashes are concatenated in order, then hashed.
Relationship condition context
IRelationshipCondition.Context is a JsonNode. It is hashed through a canonical JSON text, so semantically equal JSON hashes equally:
- Object properties are sorted by ordinal (
StringComparer.Ordinal) key, recursively at every depth. - Arrays keep their order.
- Primitives use their JSON text, so
1and1.0hash differently, as do1and"1". - Strings are re-escaped uniformly, so
"\u00e9"and"é"hash equally. - Whitespace is dropped.
- A property with a JSON
nullvalue differs from an absent property.
The canonical form is produced with JsonDocument and Utf8JsonWriter only, so it does not use reflection-based serialization and stays AOT-safe.
Dependencies
Pure.Authorization.OpenFGA.Model.Abstractions— OpenFGA authorization model interfacesPure.HashCodes— deterministic, byte-enumerable hash computation over Pure primitives
Target Frameworks
- .NET 7
- .NET 8
- .NET 9
- .NET 10
Installation
Not yet published. Once released:
dotnet add package Pure.Authorization.OpenFGA.Model.HashCodes
Usage
using Pure.Authorization.OpenFGA.Model.HashCodes.Model;
using Pure.Authorization.OpenFGA.Model.HashCodes.Tuple;
IAuthorizationModel model = ...; // any IAuthorizationModel implementation
ITuple tuple = ...; // any ITuple, with or without a condition
// Enumerate the hash bytes directly
byte[] modelHash = new AuthorizationModelHash(model).ToArray();
// Compare two models structurally, regardless of the order of their types
bool same = new AuthorizationModelHash(modelA).SequenceEqual(
new AuthorizationModelHash(modelB)
);
// Hash a tuple; a conditional tuple includes its condition and context
byte[] tupleHash = new TupleHash(tuple).ToArray();
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Pure.Authorization.OpenFGA.Model.Abstractions (>= 0.1.0-preview.0.1.0)
- Pure.HashCodes (>= 2.1.0)
-
net7.0
- Pure.Authorization.OpenFGA.Model.Abstractions (>= 0.1.0-preview.0.1.0)
- Pure.HashCodes (>= 2.1.0)
-
net8.0
- Pure.Authorization.OpenFGA.Model.Abstractions (>= 0.1.0-preview.0.1.0)
- Pure.HashCodes (>= 2.1.0)
-
net9.0
- Pure.Authorization.OpenFGA.Model.Abstractions (>= 0.1.0-preview.0.1.0)
- Pure.HashCodes (>= 2.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-preview.0.1.0 | 111 | 10/23/2026 |