KoniKode · Software built with intention

KNOWLEDGE BASE

Documentation

DOCUMENTATION
CURRENT APPLICATIONClean Framework
DocumentationClean Framework

Clean Framework · DOCUMENTATION

Reusable utility modules

Reusable utility modules

Bounded candidate reference — 2026-10-20: 0.2.0-rc.1 is committed locally through 84f415b3098af5653577b2c65eea31fca21df79c, unpublished and not production-approved; stable PHP >=8.5.11 <8.6.0 is required. This is documentation, not adoption: this knowledge base retains its installed 0.1.0-rc.1 dependency and PHP 8.2.11 local profile. Dated PHP 8.2 evidence is historical; earlier dirty-source reports retain their original test provenance, not retroactive clean-commit proof. Candidate examples are illustrative: API signatures were reviewed, but candidate code was not executed by this editorial update. See current checkpoint, open criteria and adoption boundary.

Read audit search and scanner/quarantine for current APIs/limits. Missing audit history retains compatible [], not loss detection. Native adapters remain explicitly selected, not default activation.

These modules are application-neutral mechanics recovered by the complete source-service audit. They use explicit dependencies and contain no Prosperous Pairs routes, tables, roles, prompts, copy, credentials, or provider accounts.

Cache

CacheStore is the application-facing get/set/delete/remember contract. FileCacheStore is an atomic JSON adapter for one host and one private directory. A zero TTL does not expire; negative TTLs and non-JSON values are rejected. remember() serializes a local cache fill. Multi-node deployments should bind a shared store rather than expecting local files to synchronize.

$cache = new FileCacheStore($root . '/storage/cache', new SystemClock(), 'catalog');
$cache->set('featured', ['ids' => [1, 2]], 300);
$featured = $cache->get('featured', ['ids' => []]);

JSON and validation

ValidationErrors collects unique, client-safe field messages. JsonEnvelope creates stable arrays and then delegates encoding to Response; it never writes headers or output on its own.

$errors = new ValidationErrors();
$errors->add('email', 'Enter a valid email address.');

return JsonEnvelope::response(
    JsonEnvelope::failure('The request is invalid.', $errors->all(), 'validation.failed'),
    422
);

The application still owns validation rules, wording, error codes, and HTTP status selection.

Mail

MailMessage validates recipients, subjects, and non-routing headers and keeps plain/HTML alternatives immutable. Mailer is a delivery port. Bind an application adapter for the selected provider; NullMailer must be an explicit test or mail-free choice, not an accidental production default.

Audit and feature decisions

For one app-owned activation policy across HTTP/CLI/workers/schedules, use the opt-in ModulePolicy layer. It reuses the gates below, defaults optional modules OFF and separates readiness from user authorization.

AuditEvent carries bounded, scalar, already-redacted metadata to an application AuditSink. Never put credentials, request bodies, raw tokens, or private media into audit metadata. The framework does not choose a table, retention period, or remote audit provider.

The optional FileAuditSink now uses the owner-approved bounded search and strict private-file profile. Its native Linux/effective-owner and 0700/0600 requirements are adapter-specific, not a framework-wide logging or OS rule. Existing unsafe paths refuse without automatic permission or history repair; broader deployment profiles remain unqualified until separately implemented and tested.

StaticFeatureGate is deny-by-default configuration. CompositeFeatureGate requires every configured environment/config/provider/ policy/authorization condition and returns health plus the in-flight disable disposition. Application-owned conditions retain domain targeting and audit policy.

Outbox contracts

OutboxMessage provides stable identity, schema version, stable message key, occurrence/availability time, destination, and a JSON-compatible payload. DatabaseOutboxStore, OutboxDispatcher, and OutboxReconciliationService provide the initial lease/retry/dead-letter and unknown-result flow. Applications must enqueue with the same connection inside the domain transaction and still prove real database concurrency and provider semantics. See QUEUE_AND_SCHEDULER.md.

Structured AI

StructuredAiProvider remains the concise legacy application-adapter boundary. The typed Phase 8 path adds immutable prompt/schema/request/result values, per-request budgets, fixed-window quota reservation, a typed provider port, independent closed-schema validation, deterministic fakes, and a no-retry broker. CircuitBreaker still tracks consecutive failures through a CircuitStateStore and injectable Clock.

src/AssistedAction/ can turn one validated result into a stored proposal for an explicitly registered application handler. Proposal creation does not run the handler. Execution independently requires exact permission/scopes, risk-based recent authentication, action-bound approvals, optimistic ownership, a stable idempotency key, and redacted audit. Ambiguous handler failures become unknown and are not automatically replayed. See AI_AND_ASSISTED_ACTIONS.md.

Neither path selects a provider/model, reads credentials, supplies product prompts/actions, or discovers tools. Multi-process deployments must provide atomic shared circuit, quota, and action stores.

Private local storage and protected delivery

LocalPrivateStorage stores bounded bytes outside public/ under random opaque keys and private permissions. ProtectedFileService asks the application's PrivateObjectPolicy before resolving the key, conceals denied objects by default, rechecks root containment through Response::file, supports a single byte range, and marks output private/no-store.

use Clean\Framework\Http\RequestAttribute;
use Clean\Framework\Storage\LocalPrivateStorage;
use Clean\Framework\Storage\ProtectedFileService;

// The root, inspected upload, request, policy, and trusted object metadata
// come from the application; this fragment does not authenticate users.
$storage = new LocalPrivateStorage($root . '/storage/uploads', 25 * 1024 * 1024);
$stored = $storage->storeFile($validatedUploadPath, 'video/mp4', 'mp4');

// Persist $stored->key() in application metadata; do not expose its local path.
$files = new ProtectedFileService($storage, $applicationObjectPolicy);

return $files->response(
    $request,
    $storedKeyFromTrustedMetadata,
    $request->attribute(RequestAttribute::PRINCIPAL)
);

Authentication middleware populates framework.principal; the constant keeps this attribute name synchronized with the framework. Put authentication before delivery. An absent principal is null, not an identity with implicit permission. The object policy still decides whether that caller may read this exact object.

Opaque names and a private directory are not authorization. The application must persist ownership/visibility metadata, enforce object-level policy, limit uploads at the web server and PHP layers, verify actual content, scan where appropriate, define retention/deletion, and test its deployment filesystem.

Media signature detection

MediaTypeDetector reads at most sixteen leading bytes and recognizes the MP4, QuickTime, and WebM containers used by the source workflow. This is only an allowlist signal. It does not prove that codecs are playable, content is safe, duration/audio are present, or the file is free from malicious payloads. Full inspection/scanning/transcoding remains a separate application decision.

Editorial provenance

Source: docs/UTILITY_MODULES.md. Source SHA-256: a68da85b1fe7f4175b6d6cd87e733c03e62afa569b8786498df7a17e36c5f4df.

Framework candidate reference; editorial checkpoint 2026-10-20. See candidate status and adoption boundary.

EXPLORE FURTHER

Drag a divider or use Left and Right Arrow keys to resize. Hold Shift for larger steps. Enter hides or restores the sidebar; Home and End select its minimum or maximum width. Double-click resets the width.

Install KoniKode