icom/ framework

An explicit tenant boundary.

Fresh memberships, resource-tenant checks and a small audit record with fixed fields.

Create a tenant and manage membership

Authenticated application code supplies the actor identity. Tenants::create makes that user an owner. Only a current tenant owner can add, change or remove members. Ownership changes are serialized and cannot remove the final owner. Global admin roles do not grant tenant membership automatically.

example
use Icom\Auth\Tenants;

$tenantId = $tenants->create($authenticatedUser, 'Workspace');
$tenants->setMember($authenticatedUser, $tenantId, $memberId, 'editor');
$tenants->setMember($authenticatedUser, $tenantId, $memberId, null); // revoke

Authorize the actual resource

A tenant ID in a route or request is a selector, never proof of access. Fetch the resource with a tenant-qualified query, then pass its stored tenant_id to authorize(). The helper checks that it equals the requested tenant, that the account remains active and that fresh membership grants the ability. Configure additional tenant roles through a container factory if needed.

example
$note = $db->table('tenant_notes')
    ->where('tenant_id', $tenantId)->where('id', $noteId)->first();
if (!$note) throw new \Icom\Http\HttpException(404, 'Note not found.');
$tenants->authorize($user, $tenantId, 'notes.update', $note['tenant_id']);
// Keep the same tenant filter on the subsequent write.

No automatic query scoping

The example tenant_notes table belongs to your application; it is not the demonstration notes schema. The helper does not silently rewrite SQL, isolate physical databases, derive trusted tenants from headers or replace object-level ownership policies. Membership can change while a request is in flight; operations requiring a strict transaction-time authorization guarantee must lock and recheck membership within the write transaction.

Read security audit events

Account creation/role/password/disable changes, session issuance/logout, factor enrollment/verification/recovery and reset operations write fixed-field audit records. Tenant membership events include the tenant ID. Records contain event, outcome, timestamps and IDs; no password, OTP, secret, token, email, IP or request body. Known-account password failures and factor failures are recorded; the audit is not a complete traffic log.

example
php bin/icom audit:recent all 50
php bin/icom audit:recent ACCOUNT_ID 50

// Trusted application event; no arbitrary metadata payload:
$audit->record('invoice.approved', $user->id, $user->id, tenant: $tenantId);

Retention and trust

The application Audit API appends and reads; it has no edit/delete endpoint. The database operator still has power to change rows, so this is not a tamper-proof ledger or independently retained evidence. Define retention, export, access control and alerts for your application. No public administrative console, external audit collector or automatic retention deletion is enabled.

Developer preview — benchmark claims require reproducible evidence.

Explore the architecture