Coject.Core.Logging.Cscc
1.1.0
dotnet add package Coject.Core.Logging.Cscc --version 1.1.0
NuGet\Install-Package Coject.Core.Logging.Cscc -Version 1.1.0
<PackageReference Include="Coject.Core.Logging.Cscc" Version="1.1.0" />
<PackageVersion Include="Coject.Core.Logging.Cscc" Version="1.1.0" />
<PackageReference Include="Coject.Core.Logging.Cscc" />
paket add Coject.Core.Logging.Cscc --version 1.1.0
#r "nuget: Coject.Core.Logging.Cscc, 1.1.0"
#:package Coject.Core.Logging.Cscc@1.1.0
#addin nuget:?package=Coject.Core.Logging.Cscc&version=1.1.0
#tool nuget:?package=Coject.Core.Logging.Cscc&version=1.1.0
Coject Core Logging CSCC Adapter
Coject.Core.Logging.Cscc is the optional CSCC destination for the provider-neutral Coject logging pipeline. It registers separate pooled HTTP clients for log and audit lanes, sends canonical Contracts 3.0 JSON, and reuses Core’s bounded ownership, retry, outbox, circuit-breaker, local JSONL, redaction, and shutdown boundaries.
This package is the transport adapter only. It does not depend on CojectCore.Controller, does not register MVC controllers or routes, and does not contain the legacy flat CSCC sender. It depends on Coject.Core.Logging because the adapter implements Core’s destination handoff interface.
Coject.Core.Logging.Cscc uses Core's resolved Sprint 01 storage layout for
local application/audit lanes, Core spool, and the provider outbox. It does
not define a second storage policy.
Package and support
- Package:
Coject.Core.Logging.Csccversion1.0.1 - Target framework:
net8.0 - Required Core package:
Coject.Core.Logging1.0.0 - Core wire contract:
Coject.Core.Logging.Contracts3.0.0transitively - Repository: https://github.com/coject/CojectCore
- The package contains portable symbols (
.snupkg) and the packagedREADME.md, license, and icon.
This storage guidance is documentation for an additive contract. It does not change project/package version fields, publish either package, update a consumer, or deploy an application. Package publication and application deployment are separate approvals.
Installation
<PackageReference Include="Coject.Core.Logging.Cscc" Version="1.0.1" />
The project reference to Coject.Core.Logging is intentional and becomes a package dependency when packed. CojectCore.Controller is not a dependency: applications can use Controller and logging packages side by side without adding Controller types to the logging runtime graph.
Registration
Use the CSCC host-level extension. It registers Core and then the CSCC adapter; registration is idempotent.
using Coject.Core.Logging.Cscc;
var builder = Host.CreateApplicationBuilder(args);
builder.AddCojectLoggingCscc();
using var host = builder.Build();
await host.RunAsync();
For ASP.NET Core:
using Coject.Core.Logging.Cscc;
var builder = WebApplication.CreateBuilder(args);
builder.AddCojectLoggingCscc();
var app = builder.Build();
app.MapGet("/", () => Results.Ok("running"));
app.Run();
AddCojectLoggingCscc() calls the Core registration boundary and registers the adapter only when CojectLogging:Cscc:Enabled is exactly true. When disabled, the application remains local-only and no CSCC HTTP clients or remote calls are created. The optional host argument is the same Core coexistence switch:
builder.AddCojectLoggingCscc(options =>
options.ReplaceExistingFrameworkProviders = false);
The default is true, which removes previously registered framework ILoggerProvider instances so there is one ownership path. Set it to false only for a deliberate migration.
Complete appsettings.json tree
This is the complete safe configuration graph used by Core and the CSCC adapter. CSCC is disabled here, so endpoint, key, and scope values are omitted.
{
"CojectLogging": {
"Environment": "Production",
"TrustedContext": {
"KnownProxies": [],
"KnownNetworks": [],
"ForwardLimit": 1,
"PresentationTimeZoneId": "Asia/Riyadh"
},
"Local": {
"RootPath": "logs",
"Application": {
"Enabled": true,
"RetentionDays": 30,
"MaxSegmentSizeBytes": 104857600,
"BatchSize": 100,
"FlushIntervalMilliseconds": 1000
},
"Audit": {
"Enabled": true,
"RetentionDays": 365,
"MaxSegmentSizeBytes": 104857600,
"BatchSize": 20,
"FlushIntervalMilliseconds": 500
},
"Storage": {
"Instance": {
"Mode": "Generated",
"Id": null,
"ConflictPolicy": "Fail"
},
"Paths": {
"InstanceRoot": "{root}/{service}/{environment}/{instance}",
"LocalRoot": "{instanceRoot}/local",
"ApplicationRoot": "{localRoot}/application",
"AuditRoot": "{localRoot}/audit",
"QuarantineRoot": "{laneRoot}/quarantine",
"SpoolRoot": "{instanceRoot}/spool",
"LogSpoolRoot": "{spoolRoot}/logs",
"AuditSpoolRoot": "{spoolRoot}/audits",
"ProviderOutboxRoot": "{root}/outbox",
"OutboxQuarantineRoot": "{outboxLaneRoot}/quarantine",
"OutboxArchiveRoot": "{outboxLaneRoot}/archive"
},
"Files": {
"ApplicationSegment": "application-{service}-{environment}-{instance}-{segment}.jsonl",
"AuditSegment": "audit-{service}-{environment}-{instance}-{segment}.jsonl",
"WriterLock": ".writer.lock",
"InstanceOwnershipLock": ".ownership.lock",
"OutboxMetadata": "outbox-metadata.json"
}
}
},
"Cscc": {
"Enabled": false
},
"Queues": {
"Memory": {
"Logs": {
"MaxRecords": 10000,
"MaxBytes": 134217728
},
"Audits": {
"MaxRecords": 1000,
"MaxBytes": 268435456
}
},
"Overflow": {
"Logs": {
"MaxRecords": 25000,
"MaxBytes": 536870912
},
"Audits": {
"MaxRecords": 5000,
"MaxBytes": 1073741824
},
"HighSeverityReservationPercent": 25
},
"HighSeverityBurstLimit": 8,
"EnqueueTimeoutMs": 25,
"HandoffRetryInitialDelaySeconds": 1,
"HandoffRetryMaxDelaySeconds": 60,
"ShutdownDrainTimeoutSeconds": 10
},
"Outbox": {
"Retry": {
"InitialDelaySeconds": 2,
"Multiplier": 2,
"MaxDelaySeconds": 300,
"RetryAfterMinimumSeconds": 1,
"RetryAfterMaximumSeconds": 3600
},
"Logs": {
"MaxRecords": 100000,
"MaxBytes": 2147483648,
"HighSeverityReservationPercent": 25,
"QuarantineRetentionDays": 90
},
"Audits": {
"MaxRecords": 20000,
"MaxBytes": 4294967296,
"HighSeverityReservationPercent": 25,
"QuarantineRetentionDays": 365
}
},
"Redaction": {
"AdditionalSensitiveFields": []
},
"Health": {
"EmergencyDiagnosticMinimumIntervalSeconds": 60,
"Disk": {
"WarningFreeSpacePercent": 10,
"WarningFreeSpaceBytes": 2147483648,
"CriticalFreeSpacePercent": 5,
"CriticalFreeSpaceBytes": 536870912,
"CheckIntervalSeconds": 30
}
},
"Framework": {
"Enabled": true,
"MinimumLevel": "Information"
}
}
}
Property behavior and limits
Environmentdefaults toProductionand must contain 1–64 printable characters.TrustedContext.KnownProxiescontains canonical proxy IPs,KnownNetworkscontains aligned CIDR networks, and each list has at most 100 unique entries.ForwardLimitis 1–8.PresentationTimeZoneIddefaults toAsia/Riyadhand changes presentation only.Local.RootPathis a safe path resolved from the process working directory. It cannot contain control characters, wildcards, or... Local application and audit sinks are mandatory in the binding boundary; a missing or false sink binding is normalized to its enabled default in this release. Keep bothEnabledvalues true.- Local
RetentionDaysis 1–3650, segment size is 1–1073741824 bytes,BatchSizeis 1–1000, and flush interval is 50–5000 milliseconds. - With
Cscc.Enabledfalse, Core is local-only and endpoint/key/scope identity values are not required. With it true,AppIdis a non-empty identifier of at most 128 characters;Endpointis an absolute HTTPS URI without user information;ApiKeyis one scalar value of 32–512 non-whitespace characters; andContractVersionmust be exactly3.0. - Supported scopes are exactly
logging.writeandaudit.write. Scopes must be unique and includelogging.write; includeaudit.writefor audit delivery. Cscc.Delivery.LogConcurrencyandAuditConcurrencyare 1–32, combined maximum 64, andAttemptTimeoutSecondsis 1–60.CircuitBreaker.FailureThresholdis positive; cooldown and rate-limit values are positive finite numbers; maximums cannot be below minimums; andJitterRatiois greater than zero and at most 1.- Memory capacities are 1–1000000 records and 1–4294967296 bytes.
HighSeverityBurstLimitis 1–100, enqueue timeout is 1–250 milliseconds, handoff retry delays are 1–60 and 1–300 seconds with maximum not below initial, and shutdown drain is 1–30 seconds. - Overflow and outbox
HighSeverityReservationPercentis fixed at 25 in v1. Outbox capacities are 1–10000000 records and 1–17179869184 bytes; quarantine retention is 1–3650 days. - Outbox retry delays and Retry-After bounds are positive finite values no greater than 3600 seconds; multiplier is 1–10; maximums cannot be below minimums.
Redaction.AdditionalSensitiveFieldsaccepts at most 100 non-empty printable names of at most 128 characters.HealthandDiskenforce positive thresholds, critical thresholds below warning thresholds, a diagnostic interval of 1–3600 seconds, and disk checks of 5–300 seconds.Framework.EnabledandMinimumLevelapply only to the compatibilityILoggerProvider; explicit typedICojectLoggercalls are not filtered by this minimum level.
Sprint 01 storage contract
Storage is resolved once by Core and shared by this adapter. Local.Storage
is additive, and omitting it preserves the existing paths and names.
Local.RootPath remains the compatibility alias for {root}. The CSCC
adapter does not define a second root or bypass Core's resolver.
Generated is the default instance mode and creates a new safe instance per
process start. Stable is explicit opt-in and requires a safe, non-empty
operator Instance.Id; a restart with the same identity reopens the same
local application/audit roots and continues valid segments. Stable identity
is exclusive: the owner must acquire the existing ownership locks before
writing, repairing, rotating, cleaning, or recovering. With the default
ConflictPolicy: "Fail", a conflict returns a bounded failure; with
ConflictPolicy: "Defer", it returns a bounded deferred disposition. Both
policies leave the owned storage untouched and never silently switch to a
generated identity. Safe health/startup reporting uses the path-free codes
STABLE_OWNERSHIP_CONFLICT or STABLE_OWNERSHIP_CONFLICT_DEFERRED.
Previous-instance recovery does not import the currently owned stable
instance.
The closed token set is {root}, {service}, {environment}, {instance},
{instanceRoot}, {localRoot}, {spoolRoot}, {laneRoot},
{outboxLaneRoot}, and {segment}. Path templates resolve only in dependency
order and must stay inside the approved root. Unknown or malformed tokens,
cycles, absolute child segments, .., wildcards, control characters, empty
values, collisions, and symlink/reparse-point paths fail closed. File-name
templates cannot contain separators, wildcards, control characters, reserved
device names, or unknown tokens. Application and audit segment names contain
exactly one {segment} token; the library owns the eight-digit sequence and
all ready/inflight/temporary/quarantine/archive/reason/metadata/lock
semantics.
With the safe example RootPath: "logs", the resulting layout is:
<working-directory>/logs/<service>/<environment>/<instance>/
local/application/
application-<service>-<environment>-<instance>-00000001.jsonl
.writer.lock
quarantine/
local/audit/
audit-<service>-<environment>-<instance>-00000001.jsonl
.writer.lock
quarantine/
spool/
.ownership.lock
logs/
audits/
quarantine/logs/
quarantine/audits/
<working-directory>/logs/outbox/
outbox-metadata.json
Cscc/logs/
<entry>.ready | <entry>.inflight
quarantine/<entry>.quarantine
archive/<entry>.archive
Cscc/audits/
<entry>.ready | <entry>.inflight
quarantine/<entry>.quarantine
archive/<entry>.archive
For one deployment that must keep fixed application and audit roots across restarts, use the same paths with:
{
"CojectLogging": {
"Local": {
"Storage": {
"Instance": {
"Mode": "Stable",
"Id": "primary",
"ConflictPolicy": "Fail"
}
}
}
}
}
This resolves to logs/<service>/<environment>/primary/local/application
and logs/<service>/<environment>/primary/local/audit. Do not remove
{instance} while using Generated, because that would merge unrelated
process histories.
Local JSONL behavior remains unchanged under custom names: continue from the
highest valid segment, append below MaxSegmentSizeBytes, rotate safely when
full, and quarantine only an incomplete or invalid tail before appending.
Retention matches only files owned by the active template and never deletes
the active segment or unrelated files. Core spool and the CSCC outbox retain
atomic writes, ownership, checksums, bounded recovery, retry, quarantine,
archive, and retention behavior. Malformed, unsafe, permanently rejected, or
checksum-invalid artifacts are quarantined with safe reasons; uncertain
remote outcomes remain available for reconciliation. Graceful shutdown drains
within its configured bound and spills unresolved ownership.
When Cscc.Enabled is false, Core still writes local application and audit
JSONL, rotates and retains segments, quarantines invalid data, and recovers
the Core spool. This adapter creates no CSCC HTTP clients or remote calls and
does not attempt provider outbox delivery. Enable CSCC only through reviewed
deployment configuration with endpoint and API key values supplied by a
secret provider; never place credentials in this README or appsettings.json.
Template changes are migrations, not live edits. Stop all owners gracefully, verify the stable lock is released, back up the complete managed root, and validate the new template before cutover. The safe default is to start a new stream and retain the old tree read-only. Continuing an existing stream requires an intentional operator migration of only validated compatible segments/artifacts, with canonical bytes, timestamps, ordering, and collisions verified before the source is retired. Existing generated instances are never renamed or deleted automatically.
See the Core migration procedure and the S01-009 handoff for the full operator sequence.
CSCC contract and transport
The adapter uses the configured endpoint as a base and posts:
- logs to
/api/v3/ingest/logs - audits to
/api/v3/ingest/audits
Requests have Content-Type: application/json, X-API-Key, and X-Correlation-ID headers. The body is the exact redacted canonical Contracts 3.0 UTF-8 record produced by Core. The root is nested, not the legacy flat audit object:
{
"envelope": {
"contractVersion": "3.0",
"eventType": "DataModification",
"actionType": "Update",
"occurredAt": "2026-01-01T12:00:00.000Z",
"serviceName": "SampleService",
"environment": "Production",
"module": "Orders",
"resource": "Order",
"operation": "Update",
"entityId": "123",
"correlationId": "sample-correlation-123",
"actor": {
"type": "User",
"id": "42"
}
},
"payload": {
"oldValues": {},
"newValues": {},
"changedFields": ["Status"]
}
}
The provider treats HTTP 201 as success only when the JSON acknowledgement contains success: true, a string recordId, the matching correlationId, and a contractVersion. Response bodies are bounded and only safe error codes are retained; credentials and arbitrary provider text are not copied into diagnostics.
The legacy CsccLogService shape is not used by this package. Do not send a flat { username, actionType, eventType, changes, metadata, ... } object to the v3 endpoint.
Typed logging and auditing
The CSCC package exposes Core’s ICojectLogger through its Core dependency:
using Coject.Core.Logging.Contracts;
using Coject.Core.Logging.Contracts.Payloads;
var logger = host.Services.GetRequiredService<ICojectLogger>();
var result = await logger.LogAsync(
CojectEvents.SystemExecution(new SystemExecutionPayload(stepName: "cscc-startup")),
CojectLogLevel.Info,
"CSCC logging verification");
Console.WriteLine($"{result.Status}: {result.Reason}");
Audits must be created with closed Contracts factories and submitted after the business transaction commits:
CojectAuditAction<DataModificationPayload> action =
CojectAuditActions.DataModification(
new DataModificationPayload(
oldValues: oldPersistedSnapshot,
newValues: newPersistedSnapshot));
var result = await logger.AuditAsync(action);
The trusted context provider must supply Module, Resource, and Operation for audits. A missing or incomplete snapshot, invalid action/event pair, or no-change modification is rejected before any CSCC request.
Local JSONL and audit behavior
CSCC delivery never replaces local persistence. Core writes separate application and audit lanes under:
<working-directory>/logs/<service>/<environment>/<instance>/local/application/
<working-directory>/logs/<service>/<environment>/<instance>/local/audit/
Files are named application-...-00000001.jsonl and audit-...-00000001.jsonl. Each lane has its own queue, writer lock, batch/flush policy, segment rotation, retention, and quarantine directory. The local audit body is the same canonical Contracts 3.0 record sent to CSCC.
Failure, retry, spool, and circuit behavior
The Core enqueue boundary is bounded by Queues.EnqueueTimeoutMs. Accepted immutable records enter independent log/audit memory lanes. Overflow uses the protected instance spool:
<instance-root>/spool/logs/
<instance-root>/spool/audits/
CSCC then has independent log and audit HTTP lanes with LogConcurrency, AuditConcurrency, and AttemptTimeoutSeconds. Provider outbox state is separate from the Core spool. Unresolved records remain durable until confirmation, permanent failure, quarantine, or an explicit reconciliation decision.
Transient network failures, timeouts, HTTP 408/425, HTTP 429, and HTTP 5xx responses are retried within configured bounds. A valid integer Retry-After on 429 is honored for up to one hour and constrained by the configured pause limits. The circuit breaker opens after the configured consecutive transient-failure threshold and uses bounded cooldown with jitter.
When a log or audit attempt raises a transport exception or reaches its bounded
timeout, the adapter emits one fixed-content warning through Core's existing
local-only ILogger compatibility path. Core records that warning as typed
SystemExecution telemetry without re-entering CSCC; the original handoff
result, retry/ownership behavior, and redaction boundary are unchanged. The
warning does not contain endpoint, API key, exception text, response content,
JWT, or payload data.
HTTP 401 is an authentication failure and HTTP 403 is a scope failure. HTTP 400, 404, 405, 413, 415, 422, and other ordinary 4xx responses are permanent failures. In particular, HTTP 422 is classified as PERSISTENCE_VALIDATION_FAILED; it does not open the breaker and is not blindly retried. HTTP 409 and selected client-disconnect/transport cases are uncertain and retained for reconciliation because delivery may have committed remotely.
On graceful shutdown, Core closes intake, drains within ShutdownDrainTimeoutSeconds, and spills unresolved ownership. Await host shutdown so durable handoff can finish.
Redaction and security
- Use a secret manager or environment-variable override for
CojectLogging:Cscc:ApiKey. Never put a real key inappsettings.json, source, README, logs, package contents, or issue reports. - The key is sent only as
X-API-Key. It is not included in the canonical envelope, local record, outbox metadata, or health snapshot. - Use an absolute HTTPS endpoint with no user information. The adapter does not follow redirects and does not use cookies.
- Core redacts built-in sensitive names before local and CSCC delivery. Add application-specific names through
Redaction.AdditionalSensitiveFields; do not try to disable built-in redaction. - Restrict scopes to
logging.writeandaudit.write, grant only the lanes required by the application, and keepAppIdstable and non-secret. - Configure
KnownProxiesandKnownNetworksonly for trusted infrastructure. Forwarded IP values must be rebuilt by that edge and must match the configured trust policy. - System actors do not carry producer actor IPs. User and anonymous actors may carry a trusted normalized originating address. Correlation IDs are validated and transport headers must agree with the envelope.
Correlation, actor, and IP behavior
Core prefers a valid X-Correlation-ID, then an envelope correlation ID, then generates one. If both are supplied they must match. IDs are 8–64 safe ASCII characters. Canonical timestamps are UTC at millisecond precision; PresentationTimeZoneId is display-only.
The actor model supports user, system, and anonymous actors. User/system actors require stable IDs and display names are bounded. Request context is closed to correlationId, module, resource, operation, entityId, and statusCode; arbitrary client fields cannot overwrite server-owned context. Status codes are limited to 100–599.
Minimal verification
- Install
Coject.Core.Logging.Csccand registerAddCojectLoggingCscc(). - Set
Cscc.Enabledtotrue, supply the endpoint and key through deployment configuration/secret storage, and set scopes appropriate to the enabled lanes. - Run the
SystemExecutionexample and confirm both a local application JSONL record and a CSCC log request. - Submit a closed audit action after commit with module/resource/operation context and confirm the local audit record plus a
201CSCC acknowledgement. - Inspect
ICojectLoggingHealthfor safe queue, spool, outbox, disk, and circuit state. Do not inspect or print the API key.
Troubleshooting
| Symptom | Check |
|---|---|
| No CSCC HTTP activity | Confirm the adapter package is installed, AddCojectLoggingCscc() is called, Cscc.Enabled is true, and startup validation succeeds. |
| Startup validation fails | Check exact ContractVersion 3.0, absolute HTTPS endpoint, scalar 32–512 character key, unique supported scopes, delivery bounds, and circuit-breaker bounds. |
| HTTP 401 | The API key is missing, invalid, expired, or not associated with the configured application. Rotate it through the secret provider; do not put it in source. |
| HTTP 403 | Grant the required logging.write or audit.write scope for the failing lane. |
| HTTP 422 | This is a permanent provider validation failure. Verify the server accepts the canonical nested { "envelope": {}, "payload": {} } Contracts 3.0 root, event/action pair, required audit context, and payload completeness. Do not add blind retry. |
| HTTP 201 but no confirmation | The response must be JSON with success: true, recordId, matching correlationId, and contractVersion; otherwise it is treated as an invalid success response and retained as failure. |
| HTTP 429 or 5xx | Check Retry-After, circuit state, outbox quota, endpoint availability, and retry bounds. These statuses are retryable within policy. |
| Local records exist but CSCC does not | This is expected when CSCC is disabled or provider delivery is unavailable. Inspect outbox/spool health and quarantine without deleting records manually. |
| Audit is rejected before transport | Use a closed Contracts audit factory, submit after commit, provide old/new snapshots as required, and supply module/resource/operation context. |
Dependency decision
The CSCC project references Coject.Core.Logging as a project reference because it implements Core’s ICojectDestinationHandoffAdapter boundary and consumes Core options, queues, outbox, and destination handoff types. It does not reference CojectCore.Controller; no Controller API is required for transport registration or delivery.
| 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 was computed. 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. |
-
net8.0
- Coject.Core.Logging (>= 1.1.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Http (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Patch release for bounded CSCC transport-failure and timeout warning telemetry through Core's existing local-only ILogger compatibility path. Preserves handoff results, redaction, and separate log/audit HTTP clients.