internal

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package internal is every implementation of the audit module. Nothing outside modules/audit can import it, which is the compiler enforcing idea 3.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterRoutes

func RegisterRoutes(api *httpx.API, svc contracts.Service)

RegisterRoutes mounts the two routes an append-only trail has. They are written by hand rather than mounted from a rest.Spec, and that is the shape of this module rather than an omission: a Spec is five routes and three of them write. A trail nobody can create, update or delete a row in is not a resource with three operations turned off — it is a different thing, and saying so here is cheaper than a Spec with three holes in it. Both answer through kit/rest's mapping, so a 404 means what it means everywhere.

func Retention

func Retention(tenants jobs.TenantLister, days int) jobs.Job

Retention is the module's periodic work: the trail forgets what it is no longer obliged to keep. It is a job and not a subscription because nothing happens when a row expires — the clock passes, which is the distinction docs/adr/0004 draws. It publishes nothing, like everything else here.

Types

type Service

type Service struct{}

Service is the audit trail. It has no fields: everything a command needs arrives with the transaction, so one instance serves a request and an event handler at once.

func NewService

func NewService() *Service

NewService returns the trail. module.go constructs it.

func (*Service) Get

func (s *Service) Get(_ context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Event, error)

Get is one row of this tenant's trail. A row another tenant owns is not found, which is the only thing the API may say about it.

func (*Service) List

List is a page of this tenant's trail, newest first. It is a hand-written query and not a crud.List because two of the filters are range comparisons, which kit/crud's equalities cannot express — the same reason the SLA sweep writes its own. The count and the page come from separate statements, because GORM carries clauses forward and a Count that inherited the LIMIT would count the page; the order ends in the id, so two rows recorded in one transaction neither overlap nor skip between pages.

func (*Service) Record

func (s *Service) Record(_ context.Context, tx db.Tx[db.Tenant], ev events.Event) error

Record writes one event into the trail. The insert is by hand because the row is not a crud.Entity — nothing here is updated or soft-deleted — and because the conflict clause is the point: recording is idempotent by the tenant and the event's own id, the second lock behind the kernel's handled table. That one claims each delivery of each subscription; this one covers what it cannot, which is an operator replaying an outbox row or a handler that failed after writing. The tenant is part of the key because the thing being made idempotent is a row in one tenant's trail — see migrations/000015.

The tenant comes from the transaction and not from ev.TenantID. The two always agree, because Consume opened this transaction in the event's own tenant; taking it from the transaction anyway is what makes the isolation the database's rather than a struct field's.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL