Penghou.Zhinu.OpenTelemetry
0.2.0-preview.1
dotnet add package Penghou.Zhinu.OpenTelemetry --version 0.2.0-preview.1
NuGet\Install-Package Penghou.Zhinu.OpenTelemetry -Version 0.2.0-preview.1
<PackageReference Include="Penghou.Zhinu.OpenTelemetry" Version="0.2.0-preview.1" />
<PackageVersion Include="Penghou.Zhinu.OpenTelemetry" Version="0.2.0-preview.1" />
<PackageReference Include="Penghou.Zhinu.OpenTelemetry" />
paket add Penghou.Zhinu.OpenTelemetry --version 0.2.0-preview.1
#r "nuget: Penghou.Zhinu.OpenTelemetry, 0.2.0-preview.1"
#:package Penghou.Zhinu.OpenTelemetry@0.2.0-preview.1
#addin nuget:?package=Penghou.Zhinu.OpenTelemetry&version=0.2.0-preview.1&prerelease
#tool nuget:?package=Penghou.Zhinu.OpenTelemetry&version=0.2.0-preview.1&prerelease
Penghou.Zhinu
Penghou.Zhinu is a lightweight, embedded durable workflow engine for .NET. It persists workflow and step state in SQLite, allowing applications to recover from crashes and restarts without operating a separate workflow server, message broker, PostgreSQL cluster, Redis instance, or Docker infrastructure.
Zhinu is useful when work is expensive, long-running, or side-effecting and must not restart from zero after a process exits. Typical uses include AI and coding workflows, local automation, batch processing, media generation, and multi-stage application jobs.
ordinary async workflow code
-> durable step boundaries
-> transactional SQLite state
-> crash recovery and inspection
Why Zhinu
- Embedded: runs inside your .NET application.
- Durable: committed step results survive process loss.
- Replay-safe: completed steps are reused instead of executed again.
- Operational: includes leases, fencing, retries, cancellation, signals, progress, diagnosis, artifacts, and OpenTelemetry.
- Honest about side effects: interrupted delegates are at-least-once; stable idempotency keys support downstream deduplication.
- Host-independent: use direct construction or the optional hosted worker.
- Optional authorization: check each protected activity attempt through neutral contracts, with durable approval and fresh checks after retry or resume.
Zhinu stores current durable state rather than replaying an event history. When
a process restarts, the workflow method runs again from its entry point.
Completed StepAsync calls return their committed results, reconstructing
execution until the first unfinished boundary.
Packages
Zhinu consumes Penghou.Workflow.Abstractions, a product-neutral contract
package owned by the Penghou repository.
Version 0.1.0-preview.2 is published on NuGet. Zhinu's 0.2.0-preview.1
source candidate implements per-attempt authorization and durable approval
against that exact contract version. Hufu or another authority implementation
can supply the policy; Zhinu has no Hufu dependency. The optional
Penghou.Hufu.Workflow adapter is a subsequent integration phase.
Six packages target .NET 8 and .NET 10. The ASP.NET Core integration targets .NET 10 only. Publish the seven packages together at the same Zhinu version.
| Package | Purpose | Frameworks |
|---|---|---|
Penghou.Zhinu |
Core engine, runtime contracts and authorization hooks | .NET 8/10 |
Penghou.Zhinu.Sqlite |
SQLite state, leases, recovery and authorization checkpoints | .NET 8/10 |
Penghou.Zhinu.Hosting |
Hosted execution loop and DI | .NET 8/10 |
Penghou.Zhinu.Hosting.AspNetCore |
Liveness, readiness and diagnostics endpoints | .NET 10 |
Penghou.Zhinu.OpenTelemetry |
Trace and metric registration helpers | .NET 8/10 |
Penghou.Zhinu.Testing |
Isolated test host and store conformance suite | .NET 8/10 |
Penghou.Zhinu.Agents |
Microsoft Agent Framework checkpoint integration | .NET 8/10 |
For the common hosted setup:
dotnet add package Penghou.Zhinu.Sqlite --prerelease
dotnet add package Penghou.Zhinu.Hosting --prerelease
These commands select the latest published preview. The authorization APIs
below require 0.2.0-preview.1 or later; a main push and ordinary CI do not
publish NuGet packages. See release and upgrade instructions
for the input-free publish workflow and schema migration.
Five-minute quick start
Register SQLite, the hosted engine, and a workflow:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Penghou.Zhinu;
using Penghou.Zhinu.Hosting;
using Penghou.Zhinu.Sqlite;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddZhinuSqlite(options =>
options.DatabasePath = "zhinu.db");
builder.Services.AddZhinu(options =>
{
options.MaxConcurrentWorkflows = 4;
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
builder.Services.AddZhinuWorkflow<OrderWorkflow, OrderRequest, OrderResult>(
"process-order",
"1");
using var host = builder.Build();
await host.StartAsync();
var engine = host.Services.GetRequiredService<WorkflowEngine>();
var handle = await engine.StartHandleAsync<OrderRequest, OrderResult>(
"process-order",
"1",
new OrderRequest("order-42"));
OrderResult result = await handle.WaitAsync();
Console.WriteLine(result.Confirmation);
Define the workflow as ordinary async code with explicit durable steps:
public sealed class OrderWorkflow : IWorkflow<OrderRequest, OrderResult>
{
public async Task<OrderResult> RunAsync(
WorkflowContext workflow,
OrderRequest request,
CancellationToken cancellationToken)
{
var validated = await workflow.StepAsync(
"validate",
request,
(input, ct) => ValidateAsync(input, ct),
cancellationToken: cancellationToken);
return await workflow.StepAsync(
"submit",
validated,
(input, step, ct) => SubmitAsync(
input,
idempotencyKey: step.IdempotencyKey,
ct),
new StepOptions
{
Retry = new RetryPolicy
{
MaxAttempts = 3,
InitialDelay = TimeSpan.FromSeconds(2)
},
ExecutionTimeout = TimeSpan.FromMinutes(2)
},
cancellationToken);
}
private static Task<ValidatedOrder> ValidateAsync(
OrderRequest request,
CancellationToken cancellationToken) =>
Task.FromResult(new ValidatedOrder(request.OrderId));
private static Task<OrderResult> SubmitAsync(
ValidatedOrder order,
string idempotencyKey,
CancellationToken cancellationToken) =>
Task.FromResult(new OrderResult(
$"confirmed:{order.OrderId}:{idempotencyKey}"));
}
public sealed record OrderRequest(string OrderId);
public sealed record ValidatedOrder(string OrderId);
public sealed record OrderResult(string Confirmation);
Composable class-based steps
Large workflows can move substantial operations into keyed, independently
testable step classes without moving orchestration out of RunAsync. The
durable step key and implementation key remain separate:
public static class OrderSteps
{
public static readonly WorkflowStepReference<ValidatedOrder, OrderResult>
Submit = new(new("submit-order"));
}
builder.Services.AddZhinuStep<SubmitOrderStep>(OrderSteps.Submit);
public sealed class SubmitOrderStep(IOrderGateway gateway)
: CompensatingWorkflowStep<ValidatedOrder, OrderResult>
{
public override async Task<OrderResult> ExecuteAsync(
WorkflowStepContext context,
ValidatedOrder input,
CancellationToken cancellationToken)
{
await context.EmitAsync(
"order-submission-started",
new { input.OrderId },
cancellationToken);
return await gateway.SubmitAsync(
input,
context.IdempotencyKey,
cancellationToken);
}
public override Task CompensateAsync(
WorkflowStepContext context,
ValidatedOrder input,
OrderResult output,
CancellationToken cancellationToken) =>
gateway.CancelAsync(output.Confirmation, cancellationToken);
}
Invoke it from the visible workflow graph and explicitly opt into durable compensation:
var submitted = await workflow.StepAsync(
stepKey: "initial-submit",
step: OrderSteps.Submit,
input: validated,
stepOptions: new StepOptions
{
Retry = new RetryPolicy { MaxAttempts = 3 }
},
cancellationToken: cancellationToken,
compensation: StepCompensationMode.Enabled);
The shared WorkflowStepReference<TInput,TOutput> binds the implementation key
to its serialization contract. It lets invocation infer both generic types and
lets hosting reject an implementation with the wrong contract during
registration. The raw StepImplementationKey overloads remain available for
dynamic and custom-container scenarios.
Use the same reference for independently durable parallel work without manually
constructing a Task.WhenAll wave:
IReadOnlyList<OrderResult> results = await workflow.FanOutAsync(
"submit-orders",
OrderSteps.Submit,
validatedOrders,
stepOptions,
cancellationToken);
Fan-out keys are index-based (submit-orders.0, .1, and so on), so callers
must provide a deterministic input order. Each item has its own durable result,
retry lifecycle, scope, and optional compensation registration. Results retain
input order.
Use LoopAsync when later work depends on state produced by the previous
iteration. This is intentionally different from independent fan-out:
ReviewState final = await workflow.LoopAsync(
"refinement",
initialState,
state => state.Score < 0.90,
async (iteration, ct) =>
{
Review review = await iteration.StepAsync(
"review",
iteration.State.Draft,
(draft, step, token) => reviewer.ReviewAsync(
draft,
step.IdempotencyKey,
token),
cancellationToken: ct);
ReviewState nextState = iteration.State with
{
Draft = review.RevisedDraft,
Score = review.Score
};
return review.Approved
? iteration.Break(nextState)
: iteration.Continue(nextState);
},
new LoopOptions(maxIterations: 10)
{
TimeBudget = TimeSpan.FromMinutes(15),
Deadline = reviewWindowClosesAt
},
cancellationToken);
Conditions are evaluated before the body. Every successful body explicitly
returns either Continue(nextState) or Break(finalState). Break commits the
final state and completes normally without another condition evaluation.
Completed body steps and committed control outcomes survive replay. Restarting
a body step with dependent invalidation preserves earlier iterations and reruns
that and later iterations. If the condition remains true after the configured
maximum, the workflow fails with LoopLimitExceededException and records
durable limit evidence. An optional Deadline is absolute. An optional
TimeBudget is wall-clock time measured from the loop's first durable entry;
Zhinu persists the resolved boundary, so a worker restart does not grant a new
budget. When both are supplied, the earlier boundary wins. Limits are checked
before uncommitted condition/body work and again before the iteration commit.
They do not forcibly interrupt user code already running; use a step
ExecutionTimeout when an individual attempt must be bounded. Perform body
work and create outcomes through the supplied iteration context so Zhinu can
preserve dependencies and reject cross-scope control.
Create a nested loop through its owning outer iteration:
ReviewState innerResult = await outer.LoopAsync(
"inner-review",
innerInitialState,
state => !state.Approved,
async (inner, ct) =>
{
ReviewState next = await inner.StepAsync(
"review",
inner.State,
ReviewAsync,
cancellationToken: ct);
return next.Approved
? inner.Break(next)
: inner.Continue(next);
},
new LoopOptions(maxIterations: 5),
cancellationToken);
The nested identity includes the outer loop and iteration. Inner loops with the
same name in different outer iterations therefore cannot collide. Inner loop
control is lexical: complete the inner loop and let the outer body explicitly
decide whether its own outcome should continue or break. Configure lexical
depth with ZhinuOptions.MaxLoopNestingDepth.
Inspect or selectively restart loop work through semantic references; callers do not need to construct Zhinu's encoded persistence keys:
WorkflowLoopReference outer = WorkflowLoopReference.Root("refinement");
WorkflowLoopReference inner = outer.Iteration(2).NestedLoop("inner-review");
WorkflowLoopProgress? progress = await handle.GetLoopProgressAsync(
inner,
cancellationToken);
WorkflowLoopStepReference target = inner.Iteration(1).BodyStep("review");
RestartPlan preview = await handle.PlanLoopRestartAsync(
target,
cancellationToken: cancellationToken);
RestartReceipt receipt = await handle.RestartLoopStepWithReceiptAsync(
target,
new RestartStepOptions
{
OperationId = commandId,
Actor = "operator",
Reason = "review evidence changed"
},
cancellationToken);
Progress groups current condition, body, and commit rows by one-based loop
iteration, summarizes committed Continue/Break outcomes and failures, and
exposes final/limit boundaries. A final false condition may be the current
observed iteration even though its body was never entered. Restart preview
remains non-mutating, while a stable operation ID makes the eventual restart
safe to retry after an ambiguous client failure.
Penghou.Zhinu.Hosting creates and asynchronously disposes a fresh DI scope
for every execution and compensation attempt. Completed-step replay creates no
scope and resolves no implementation. Step instances are ephemeral; durable
compensation state must come from the persisted input and output, not fields.
Scope disposal completes before Zhinu commits success, and disposal failure is
handled as an attempt failure. Scoped lifetime is not a distributed
transaction, so external effects still require idempotency or compensation.
Use WorkflowStep<TInput, TOutput> for execution-only steps and
CompensatingWorkflowStep<TInput, TOutput> when compensation is supported.
Enabling compensation for an execution-only implementation fails before its
forward operation runs. Duplicate registrations for the same implementation
key and contract are rejected rather than resolved by registration order.
WorkflowStepContext.EmitAsync buffers an event with the forward attempt: the
event and step result commit together, while a failed attempt publishes neither.
Manually created and compensation contexts do not offer event emission.
Microsoft DI is optional. Other containers can implement
IWorkflowStepResolver and return an IWorkflowStepLease<TStep> that owns one
attempt's scope, then configure it through
WorkflowEngineBuilder.WithStepResolver(...) or the resolver-aware
WorkflowEngine constructor.
workflowRunId is an optional idempotency key for starting the run. Repeating
the same workflow, version, input, and ID returns the existing run. Reusing the
ID with a different contract or input fails explicitly.
Administrative step restarts can also be made retry-safe. Supply a stable
RestartStepOptions.OperationId—typically the caller's approval or command
ID—and request the durable receipt:
RestartReceipt receipt = await engine.RestartStepWithReceiptAsync(
runId,
"generate",
new RestartStepOptions
{
OperationId = commandId,
Actor = userId,
Reason = "Approved regenerated output"
},
cancellationToken);
An identical retry returns the original event sequence and generation with
WasApplied == false; conflicting reuse throws
WorkflowOperationConflictException. SQLite commits the restart state, event,
and receipt atomically.
External signals support the same safe ambiguous-retry pattern without changing the existing additive API:
SignalSendReceipt receipt = await engine.SendSignalWithReceiptAsync(
runId,
"approval",
new SignalSendOptions { SignalId = responseId },
approvedPayload,
cancellationToken);
The caller keeps responseId stable across retries. SQLite atomically commits
the inbox row, signal-sent event, and durable receipt. Identical retries return
the original event with WasBuffered == false; reuse with another run, signal
name, or canonical JSON payload throws WorkflowOperationConflictException.
The receipt remains available after the inbox row is purged.
Forking and workflow evolution
A fork creates a new pending run from committed work in an existing run. The source remains unchanged. The selected step, its invalidated dependents, and incomplete work execute under the new run identity; eligible completed steps outside that boundary are copied with lineage.
WorkflowHandle<OrderResult> evolved =
await engine.ForkHandleAsync<OrderResult>(
sourceRunId,
targetStepKey: "generate",
new ForkRunOptions
{
Mode = StepRestartMode.Dependents,
WorkflowRunId = forkRequestId,
TargetWorkflowName = "process-order",
TargetWorkflowVersion = "2",
Actor = userId,
Reason = "Apply the approved planning revision"
},
cancellationToken);
Omit TargetWorkflowName and TargetWorkflowVersion to fork within the
source definition. Setting them performs a forward migration to a registered
workflow definition with the same serialized input/output contract. Completed
work is reusable only when the step key, implementation key, and input still
match. If a destination definition changes the input to an inherited completed
step, Zhinu supersedes that generation and reruns the step instead of failing
the new run or silently reusing stale output.
PlanForkAsync previews the restart boundary without creating a run.
WorkflowRunProgress.SourceRunId and retained ancestor lineage expose where
the fork came from. New deadlines are explicit; source deadlines are not
inherited.
Focused runtime interfaces
Hosted applications can depend on the smallest capability surface they need:
IWorkflowRuntimestarts and executes work and is suitable for workers or external schedulers;IWorkflowClientqueries runs, waits for completion, and sends signals;IIdempotentWorkflowClientsends retry-safe signals with durable receipts;IWorkflowAdministrationperforms administrative cancellation.
All four resolve to the same WorkflowEngine singleton when using AddZhinu.
The concrete engine remains available for typed handles and advanced inspection
or recovery operations. Caller-facing wait and signal deadlines throw
WorkflowTimeoutException; duplicate workflow or activity identities throw
WorkflowRegistrationException.
Run deadlines and step or compensation execution timeouts also persist
WorkflowTimeoutException as their durable failure type, allowing diagnostics
and recovery tools to classify time-bound failures without parsing messages.
Cancellation has two deliberately different meanings. Cancelling the token
passed to ExecuteAsync stops the current worker attempt and releases its lease;
the durable run remains Running and can resume elsewhere. CancelAsync records
terminal user or administrative intent, cancels the current run generation and
its child subtree, and fences late completion even if in-process user code
ignores cancellation. See execution semantics for the full
contract.
Delivery guarantee
Zhinu provides:
- effectively-once reuse of durably completed step results;
- at-least-once execution of a step interrupted before its result commits;
- transactional state transitions and diagnostic events;
- lease fencing so stale workers cannot commit after ownership changes.
Zhinu cannot guarantee exactly-once external side effects. A process can exit
after a remote operation succeeds but before its step result is committed. Put
external effects inside durable steps and pass WorkflowStepContext.IdempotencyKey
to downstream systems whenever possible.
Code outside durable steps may run again after recovery. Control flow should be derived from workflow input and previously committed step results.
See execution semantics and idempotency guidance for the precise contract.
Inspecting a run
A typed handle contains the common operations for one durable run:
WorkflowResult<OrderResult> snapshot = await handle.GetResultAsync();
if (!snapshot.IsTerminal)
{
WorkflowRunProgress? progress = await handle.GetRunProgressAsync();
Console.WriteLine($"{progress?.CompletedSteps} steps completed");
}
RunDiagnosis? diagnosis = await handle.DiagnoseAsync();
IReadOnlyList<WorkflowEvent> events = await handle.GetEventsAsync();
GetResultAsync returns a non-throwing snapshot for pending, completed, failed,
cancelled, and compensated runs. WaitAsync uses exception-based application
flow and returns only a successful typed result.
Subscribe to durable events and reconnect from the last observed sequence:
await foreach (var progressEvent in handle.SubscribeAsync(afterSequence: 42))
Console.WriteLine($"{progressEvent.Sequence}: {progressEvent.EventType}");
Current state is authoritative. Events support diagnostics, audit, and progress; they are not used to replay workflow execution.
Recovery and hosting
Penghou.Zhinu.Hosting continuously scans for runnable work, renews leases, and
recovers expired ownership. Without the hosting package, construct
WorkflowEngine directly and call RunAvailableAsync during startup.
The optional ASP.NET Core package exposes operational endpoints:
app.MapZhinuEndpoints("/zhinu");
GET /zhinu/livenesschecks the process without touching storage.GET /zhinu/readinessverifies that the store and schema are usable.GET /zhinu/diagnosticsreports bounded runtime and SQLite health data.
These endpoints do not expose workflow inputs, outputs, signal bodies, artifact contents, or other payload data.
Observability
Zhinu emits privacy-safe activities and metrics. Durable events remain the authoritative record of committed state. Inputs, outputs, prompts, signal payloads, SQL, and exception messages are excluded from built-in telemetry.
services.AddOpenTelemetry()
.AddZhinuInstrumentation();
See observability for source names, metrics, correlation, privacy, and cardinality conventions.
Testing
Penghou.Zhinu.Testing provides:
ZhinuTestHostfor isolated workflow integration tests;WorkflowStoreConformanceSuitefor custom durable stores;- reusable checks for concurrency, fencing, recovery, signals, artifacts, child workflows, transaction behavior, and retry-safe administration.
Store implementations must preserve the atomicity and fencing rules described in the store contract.
Advanced capabilities
The code-first runtime also supports:
- parallel steps and explicit dependency graphs;
- durable delays and external signals;
- deterministic child workflows;
- selective step restart and previewable forks;
- durable, idempotent administrative restart receipts;
- compensation, rollback, and rollback-and-restart;
- durable external-artifact references with producing-step provenance;
- run metadata, querying, pagination, retention, and bulk operations;
- schema compatibility checks and failure diagnosis.
Optional execution authorization
Configure one application-supplied IExecutionAuthorizer from
Penghou.Workflow.Abstractions. The host supplies the trusted subject mapping,
policy, store and workflow registry:
using Penghou.Workflow.Abstractions;
using Penghou.Zhinu;
var authority = new WorkflowExecutionAuthorizationOptions(
providerId: "application-policy",
bindingId: "tenant-and-policy-mapping-v1",
authorizer: authorizer);
var engine = new WorkflowEngineBuilder()
.WithStore(store)
.WithRegistry(registry)
.WithExecutionAuthorization(authority)
.Build();
Within the workflow, declare the requirements of each activity separately:
var output = await context.StepAsync(
"invoke-tool",
input,
(value, token) => tool.InvokeAsync(value, token),
new StepOptions
{
Authorization = new WorkflowAuthorizationDeclaration(
[new ExecutionRequirement("application.tools", 1, "invoke", "approved-tool")],
planId: "retained-plan", planRevision: "exact-revision")
},
cancellationToken);
The requirement vocabulary is defined by the authority adapter. Empty or
unsupported declarations confer no permission. Hosted applications use
AddZhinuExecutionAuthorization(...) alongside AddZhinu; SQLite supplies the
required store capability. Custom stores must implement
IWorkflowAuthorizationRepository for protected execution.
Allowed decisions permit the claimed attempt after current lease, generation,
revision and expiry checks. Denied, Error and Unavailable decisions prevent
callback execution. ApprovalRequired parks durably and releases the worker;
an authenticated approval wake only makes it ready for a fresh decision.
Retries and new compensation attempts also require fresh authorization.
Compensation requirements belong in StepOptions.CompensationAuthorization.
Without a provider, Zhinu preserves its existing unprotected execution profile. A protected run cannot silently resume with a missing or changed provider. Completed-step reconstruction returns history without dispatching the activity again. Orchestration code, loop bodies and arbitrary native effects remain outside activity preflight; actual tool/resource access still needs current provider checks. Read the authorization guide for configuration, declarations, approval and the exact boundary.
The runnable hosted sample demonstrates process recovery. The direct-construction sample demonstrates typed handles, signals, child workflows, artifacts, and cancellation without dependency injection.
Declarative workflows
The preview declarative layer separates portable workflow descriptions from
executable activity implementations. Applications register activities in an
ActivityCatalogue, compile against the public IActivityCatalogue contract,
and register the resulting immutable definition with the durable runtime.
Compiled definitions are treated as untrusted input at registration: Zhinu revalidates the supported topology, activity contracts, catalogue descriptors, and canonical fingerprint. Hand-authored or modified compiled artifacts cannot bypass the compiler's structural rules.
Detailed contracts:
- Execution semantics
- API conventions
- Store contract
- Observability
- Idempotency
- Trimming and Native AOT
- Public API policy
- Workflow authorization
- Release and upgrade instructions
- Roadmap
When not to use Zhinu
Zhinu is probably not the right choice when you need:
- distributed workers, multi-region availability, or a managed workflow control plane;
- a remote task queue or general-purpose message broker;
- exactly-once external side effects without downstream idempotency;
- cron scheduling as the primary product capability;
- durable storage for large files or binary artifacts;
- a model provider, autonomous agent framework, or application-specific user interface.
For those cases, use infrastructure designed for that responsibility and, when useful, compose it with Zhinu at explicit durable step boundaries.
Project direction
Zhinu is independently useful as a code-first durable workflow engine. It also implements validated declarative definitions, activity catalogues, optional authorization and durable approval. Its roadmap separates future workflow features, authority adapters and production host qualification. Natural-language methodology compilation belongs above the runtime and will be pursued only after hand-authored declarative workflows are proven.
The API is currently preview and may evolve between preview releases. Public surface changes are tracked through shipped/unshipped API baselines and package validation.
The 0.2.0-preview.1 candidate is committed
and pushed in e91804a. Local qualification passed 1,017 tests, isolated
seven-package consumers, old-binary compatibility and schema 5→6 migration
checks. SQLite upgrades require a backup and stopping older workers before
opening the database. Remote CI and user-run NuGet publication are the remaining
release gates; see the qualification record
and release instructions.
Pending Hufu integration
The separate experimental Penghou.Hufu.Zhinu.Sqlite composition retains its
narrow legacy shared-database start profile. The new neutral
Penghou.Hufu.Workflow adapter follows qualification and publication of the
Zhinu candidate. Final resource authorization, external-effect recovery and a
governed host integration remain open. Zhinu core has no Hufu dependency. See
integration phases and ownership boundaries.
License
Copyright (c) 2026 Jenő Konrád László
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 was computed. 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
- OpenTelemetry.Extensions.Hosting (>= 1.17.0)
- Penghou.Zhinu (>= 0.2.0-preview.1)
- Penghou.Zhinu.Sqlite (>= 0.2.0-preview.1)
-
net8.0
- OpenTelemetry.Extensions.Hosting (>= 1.17.0)
- Penghou.Zhinu (>= 0.2.0-preview.1)
- Penghou.Zhinu.Sqlite (>= 0.2.0-preview.1)
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.2.0-preview.1 | 26 | 10/3/2026 |
| 0.1.0-preview.15 | 32 | 10/3/2026 |
| 0.1.0-preview.14 | 52 | 9/18/2026 |
| 0.1.0-preview.13 | 55 | 9/18/2026 |
| 0.1.0-preview.12 | 79 | 9/3/2026 |
| 0.1.0-preview.11 | 77 | 8/30/2026 |
| 0.1.0-preview.10 | 70 | 8/29/2026 |
| 0.1.0-preview.9 | 71 | 8/27/2026 |
| 0.1.0-preview.8 | 80 | 8/26/2026 |
| 0.1.0-preview.7 | 71 | 8/26/2026 |
| 0.1.0-preview.6 | 92 | 8/22/2026 |
| 0.1.0-preview.5 | 79 | 8/20/2026 |
| 0.1.0-preview.3 | 75 | 8/20/2026 |