Extending Tidemark

Tidemark is designed to be extended by a separate Composer package, for example a hosted service that adds billing. Packages use these hooks and never need to edit Tidemark itself.

Entitlements

App\Contracts\Entitlements  decides what an account may do:

interface Entitlements
{
    public function canCreateSite(User $user): true|string;   // true, or a message shown to the user
    public function allowsRecording(array $site): bool;       // called for every event; keep it fast
}

The default implementation only applies TIDEMARK_MAX_SITES . A package binds its own in its service provider with $this->app->singleton(Entitlements::class, MyEntitlements::class) . When allowsRecording()  returns false , the event is dropped but the tracker still receives 202 ok , so visitors' browsers never see an error. $site  contains id , domain  and owner_id .

Events

Event When
App\Events\SiteCreated A user added a site ($site, $owner)
Illuminate\Auth\Events\Registered A new account registered

Dashboard modules

Add the URL of a JavaScript module to config('tidemark.extensions') . Its default export runs before the dashboard renders and receives an api  object:

export default function (api) {
  const { html } = api;
  api.registerAccountTab({ key: 'billing', label: 'Billing', component: Billing, dot: (user) => user.past_due });
  api.registerBanner(({ user }) => html`<div class="banner">Your trial ends soon</div>`);
  api.registerRoute('/pricing', PricingPage);
}

api.lib , api.ui  and api.chart  give access to the dashboard's helpers and components (Preact + htm, no build step needed). Account tabs appear in Account settings. The extension API is versioned (api.version ).