---
title: SAIF.NetCore.SystemHash architecture
summary: Architectural responsibilities, boundaries, and interaction patterns.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.SystemHash
reference_type: package
---

# SAIF.NetCore.SystemHash architecture

SAIF.NetCore.SystemHash is a single-class implementation-layer library in the Framework.NCore dependency graph that adapts `SAIF.NetCore.HashUtil`'s reversible salting primitive into a caller-identity credential scheme used by the Environment API and OIDC crypto settings; it is one of the three documented implementation-to-implementation exceptions in `Architecture.md` because it takes a direct NuGet package dependency on `SAIF.NetCore.HashUtil` in addition to the foundation package `SAIF.NetCore.ServiceContracts`.

## Responsibilities
- Implement `ISystemHashService` (`SAIF.NetCore.ServiceContracts`) as the repository's sole concrete `SystemHashService`, producing a SHA-256/Base64 identity hash from a `SystemType` and system name.
- Issue a salted, time-stamped credential (`ComputeHash`, `Get`) by delegating the salting mechanics to `SAIF.NetCore.HashUtil`'s `SaltShaker` while supplying the embedded create timestamp itself.
- Compute the unsalted identity hash on demand (`ComputeHashNow`) and de-salt plus validate the lifetime of a previously issued hash (`RemoveSystemHashSaltAndValidateLifetime`), throwing `SystemHashExpiredException` when expired.
- Resolve the identity assembly used to build the hash (`GetAssembly`), defaulting to `Assembly.GetCallingAssembly()` unless an assembly was supplied to the constructor.

## Component boundaries
- `SystemHashService`: the only public type in the library; it owns the SHA-256/Base64 computation, the choice of `DateTime.Now` as the embedded create date, assembly-name resolution, and the lifetime/expiry decision.
- Boundary with `SAIF.NetCore.HashUtil`: `SystemHashService` constructs `SaltShaker` directly (once with a `DateTime` to salt, once parameterless to de-salt) and constructs/throws `SystemHashExpiredException`; HashUtil owns only the salting/de-salting mechanics and never compares dates itself.
- Boundary with `SAIF.NetCore.ServiceContracts`: the library implements `ISystemHashService` and consumes the `SystemType` enum as its sole input type; it has no other contract dependency.
- Boundary with consumers: `SAIF.NetCore.ServiceExtensions` constructs `SystemHashService(Assembly)` and registers it as a singleton factory for `ISystemHashService` (used by `RegisterSecureEnvironmentClient` and inline by `RegisterDBConnectionClient`); `SAIF.NetCore.EnvironmentClient` and `SAIF.NetCore.OIDC` consume only the `ISystemHashService` interface, with no package reference to SystemHash itself.

## Interaction model
- Client-side credential issuance: a consumer calls `Get(systemType)` or `ComputeHash(systemType, systemName)`; `SystemHashService` computes the SHA-256/Base64 hash, constructs `SaltShaker(DateTime.Now)`, and calls `SaltHash` to produce the salted 120-character credential string.
- Server-side validation: a consumer calls `RemoveSystemHashSaltAndValidateLifetime(systemHashValue, expireSeconds, nowBufferSeconds)`; `SystemHashService` constructs a parameterless `SaltShaker`, calls `BlandHash` to recover the original hash and create date, compares the recovered date against the supplied lifetime, and either returns the de-salted value or throws `SystemHashExpiredException`.
- There is no inbound or outbound network call anywhere in this library; both operations are in-process cryptographic/string computation backed by `HashUtil`.
- `ISystemHashService` is reached by `SAIF.NetCore.EnvironmentClient` and `SAIF.NetCore.OIDC` purely through the `SAIF.NetCore.ServiceContracts` interface — neither library takes a package reference to `SAIF.NetCore.SystemHash` — so the only architectural NuGet edges into this library are from `SAIF.NetCore.ServiceExtensions`.

## Architecture diagram
The following diagram shows SystemHash's position relative to its one implementation-layer exception dependency, the foundation layer, and the rest of the Framework.NCore dependency graph.

```mermaid
flowchart TB
    SC["SAIF.NetCore.ServiceContracts<br>(ISystemHashService, SystemType)"]
    HASH["SAIF.NetCore.HashUtil<br>(SaltShaker, SystemHashExpiredException)"]
    SYSHASH["SAIF.NetCore.SystemHash<br>(SystemHashService)"]
    SE["SAIF.NetCore.ServiceExtensions"]
    ENVCLIENT["SAIF.NetCore.EnvironmentClient<br>(ISystemHashService consumer, interface-only)"]
    OIDC["SAIF.NetCore.OIDC<br>(ISystemHashService consumer, interface-only)"]
    APP["Consuming application"]

    SYSHASH -.->|NuGet ref| SC
    SYSHASH -.->|NuGet ref: documented exception| HASH
    SE -.->|NuGet ref| SYSHASH
    ENVCLIENT -.->|interface only, no package ref| SC
    OIDC -.->|interface only, no package ref| SC
    APP -.->|NuGet ref| SE
```

## Sequence diagram
The following diagram shows the primary salted-credential issue and validate round trip.

```mermaid
sequenceDiagram
	participant Caller
	participant SystemHashService
	participant SaltShaker

	Caller->>SystemHashService: Get(systemType)
	SystemHashService->>SystemHashService: GetAssembly() + SHA-256/Base64 hash
	SystemHashService->>SaltShaker: new SaltShaker(DateTime.Now)
	SystemHashService->>SaltShaker: SaltHash(systemHash)
	SaltShaker-->>SystemHashService: salted hash string
	SystemHashService-->>Caller: salted hash string

	Caller->>SystemHashService: RemoveSystemHashSaltAndValidateLifetime(saltedHash, expireSeconds, nowBufferSeconds)
	SystemHashService->>SaltShaker: new SaltShaker()
	SystemHashService->>SaltShaker: BlandHash(saltedHash, out createDate)
	SaltShaker-->>SystemHashService: original hash + createDate
	SystemHashService->>SystemHashService: Compare createDate against lifetime
	SystemHashService-->>Caller: value, or throw SystemHashExpiredException
```

## Design constraints
- `Get(type)` recomputes and re-salts on every call rather than caching, so each call normally returns a different string — though this is not guaranteed, since calls within the same millisecond share a timestamp and the randomized insertion choices can repeat — contradicting the class and interface XML-doc wording that implies caching*.
- `GetAssembly()` returns `Assembly.GetCallingAssembly()`, not `Assembly.GetEntryAssembly()` as its doc comment states; with the parameterless constructor this most likely resolves to the `SAIF.NetCore.SystemHash` assembly itself rather than the consuming application, making the identity non-unique per application unless the caller supplies an assembly explicitly*.
- No in-repo production code calls `ComputeHashNow` or `RemoveSystemHashSaltAndValidateLifetime`; both are intended for the external validating service (the Environment API's "GetSystemHash route")*.
- Lifetime validation uses host-local `DateTime.Now` for both the embedded create date and the validation clock, so issuer and validator must share the same local time zone and a culture that round-trips HashUtil's date format for expiry checks to be correct*.
- `SAIF.NetCore.ServiceExtensions`'s `RegisterEnvironmentClient` (non-secure path) registers `ISystemHashService` via a factory that returns `null`, so any `SystemType` secure-setting overload invoked in a non-secure startup throws a `NullReferenceException`*.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [Security and authorization](security-authorization.md)
- [API reference](api/index.md)
- [SAIF.NetCore.HashUtil overview](../saif-netcore-hashutil/overview.md)
- [SAIF.NetCore.ServiceContracts overview](../saif-netcore-servicecontracts/overview.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Sources
- `src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash.csproj`
- `src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs`
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs`, `SystemHashExpiredException.cs`
- `src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs`
- `src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs`
- `src/SAIF.NetCore.OIDC/src/SAIF.NetCore.OIDC/Services/OidcSettings.cs`
- `Architecture.md`
