tropikal-ai / connect-filament
TROPIKAL Connect integration for Laravel Filament.
Requires
- php: ^8.2
- filament/filament: ^5.0
- laravel/framework: ^11.28|^12.0|^13.0
- tropikal-ai/connect: ^0.1.3
Requires (Dev)
- laravel/pint: ^1.24
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main / 0.2.x-dev
- v0.2.17
- v0.2.16
- v0.2.15
- v0.2.14
- v0.2.13
- v0.2.12
- v0.2.11
- v0.2.10
- v0.2.9
- v0.2.8
- v0.2.7
- v0.2.6
- v0.2.5
- v0.2.4
- v0.2.3
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.15
- v0.1.14
- v0.1.13
- v0.1.12
- v0.1.11
- v0.1.10
- v0.1.9
- v0.1.8
- v0.1.7
- v0.1.6
- v0.1.5
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- dev-codex/owner-action-grants
- dev-codex/photography-owner-source-staging
- dev-codex/photography-typed-owner-actions
- dev-maintenance/filament-3
- dev-codex/pinned-chat-document
- dev-codex/embed-quality-filament-3
- dev-codex/embed-public-cache
- dev-codex/aoss-capability-provider
- dev-fix/public-proof-challenge-envelope
- dev-fix/public-booking-confirm-verification-filament3
- dev-fix/public-booking-confirm-verification
- dev-fix/embed-edge-delivery
- dev-release/v0.1.5
- dev-codex/public-embed-01
- dev-codex/public-embed-current
- dev-release/v0.1.4
- dev-feat/pr20-merge-check
- dev-codex/filament-4-compat
- dev-codex/fix-mysql-boolean-field-type
- dev-codex/connector-chat-auto-injection
- dev-release/v0.1.1
- dev-fix/embed-session-route
This package is auto-updated.
Last update: 2026-10-08 23:54:07 UTC
README
Laravel + Filament integration for TROPIKAL Connect.
tropikal-ai/connect-filament provides the Filament plugin, OAuth setup controller, encrypted installation model, Eloquent business-object discovery, explicit read/write/delete grants, signed resource API, audit logging, and optional public embed proxy endpoints. Protocol primitives live in tropikal-ai/connect.
Requirements
- PHP 8.2 or newer
- Laravel 11.28, 12, or 13
- Filament 5
- Composer 2
Install
composer require tropikal-ai/connect-filament php artisan connect-filament:install php artisan migrate
For clone-based development inside an app, add both local packages as path repositories:
{
"repositories": [
{
"type": "path",
"url": "shared/connect",
"options": {
"symlink": true,
"versions": {
"tropikal-ai/connect": "0.1.0"
}
}
},
{
"type": "path",
"url": "shared/connect-filament",
"options": {
"symlink": true,
"versions": {
"tropikal-ai/connect-filament": "0.2.0"
}
}
}
],
"require": {
"tropikal-ai/connect-filament": "^0.2"
},
"minimum-stability": "dev",
"prefer-stable": true
}
Filament Plugin
Register the plugin in your Filament panel provider:
use Filament\Panel; use TropikalAI\ConnectFilament\ConnectFilamentPlugin; public function panel(Panel $panel): Panel { return $panel ->plugin(ConnectFilamentPlugin::make()); }
OAuth One-Click Setup
Configure endpoints for the environment. Public examples use placeholder hosts:
APP_URL=https://cms.example.com CONNECT_FILAMENT_AUTHORIZATION_SERVER_URL=https://auth.example.com CONNECT_FILAMENT_CONTROL_PLANE_URL=https://control.example.com
Open the Filament navigation item labeled TROPIKAL Connect and click Connect. The package starts OAuth authorization code with PKCE, validates callback state and exact redirect URI, exchanges the code server-side, stores credentials encrypted, and registers the installation with a safe payload.
OAuth PKCE is the only supported setup path. Administrators never paste connection ids, raw tokens, signing credentials, endpoint credentials, or other secrets into the UI. There is no token-paste setup path, no copied-secret setup path, and no browser-visible credential path.
Business Object Discovery
Discovery is enabled by default, but access is opt-in. Empty grants expose nothing.
The package discovers safe Eloquent business-object candidates, excludes framework/internal/auth/security models, removes secret-shaped fields, and shows three explicit grants in Filament:
- Read
- Write
- Delete
Read grants create list/get capabilities. Write grants create create/update capabilities. Delete grants create a destructive delete capability that requires confirmation.
List capabilities support page, per_page, limit, search, and exact filters for safe readable scalar fields such as slug or category.
Optional discovery configuration:
'discovery' => [ 'enabled' => true, 'included_model_namespaces' => [ 'App\\Models\\', ], 'excluded_model_classes' => [], 'max_records_per_list_response' => 100, ],
After connecting, open TROPIKAL Connect in Filament and enable only the access needed for each discovered business object. Granted capabilities sync to the private control plane and can be used by website owner chat and automation runtimes through the same capability contract.
Public Chat Actions
A Website may expose a small, provider-owned action to its public chat without
granting resource CRUD. Implement PublicChatCapabilityProvider for the
versioned manifests and query/execute handlers. Optionally implement
PublicChatActorResolver when an action requires the Website's logged-in user.
The package registers the interfaces, signs server-to-server requests, issues short-lived encrypted actor permits, validates manifests, and makes execution idempotent. Manifests declare separate model-facing proposal and canonical provider execution schemas; query and execute payloads are rejected at the signed boundary when they do not match. The Website still owns authorization and the domain transaction; the package contains no booking or customer-specific behavior. After deploying a changed manifest, synchronize the existing installation explicitly:
php artisan connect-filament:sync
Deployment fails unless App acknowledges the exact protocol, normalized manifest hash, and accepted action kinds. Public chat actions never fall back to discovered model writes or owner capabilities.
Resource Declaration Example
Discovery can be supplemented with explicit resources in config/connect-filament.php:
'resources' => [ 'research_posts' => [ 'label' => 'Research Posts', 'model' => App\Models\ResearchPost::class, 'identifier' => 'id', 'fields' => [ 'title' => ['label' => 'Title', 'readable' => true, 'writable' => true], 'status' => ['label' => 'Status', 'readable' => true, 'writable' => true], 'published_at' => ['label' => 'Published at', 'readable' => true, 'writable' => false], ], ], ],
Only declared readable fields are returned. Only declared writable fields are accepted.
Publish risk and image fields
The same declaration is the component contract used by Website owner chat, Telegram, and other MCP clients. A component does not need a channel-specific handler. Declare its writable fields, operation risk, and any staged image field:
'resources' => [ 'news' => [ 'label' => 'News', 'model' => App\Models\News::class, 'identifier' => 'id', 'operation_risks' => ['create' => 'publish', 'update' => 'publish'], 'fields' => [ 'id' => ['readable' => true, 'writable' => false], 'title' => ['readable' => true, 'writable' => true, 'required' => true, 'max' => 255], 'content' => ['readable' => true, 'writable' => true, 'required' => true], 'img_path' => [ 'readable' => true, 'writable' => true, 'required' => true, 'type' => 'asset', 'asset' => [ 'disk' => 'public', 'directory' => 'news', 'mime_types' => ['image/jpeg', 'image/png', 'image/webp'], 'max_bytes' => 5 * 1024 * 1024, ], ], ], ], ],
An asset field is represented in the capability JSON Schema as a bounded
tropikal-asset-ref. The caller first prepares an upload, sends the bytes to a
short-lived one-time Website capability, and then supplies the opaque ref in a
normal create or update action. The Website validates, decodes, re-encodes,
scopes, and atomically consumes the staged image; binary data never enters an
MCP JSON-RPC request.
For an owner-chat-enabled installation, require idempotency at the actual mutation boundary:
CONNECT_FILAMENT_REQUIRE_MUTATION_IDEMPOTENCY=true CONNECT_FILAMENT_ASSET_TTL_SECONDS=900
Every create, update, delete, and named action then requires
X-Tropikal-Idempotency-Key. Exact retries return the durable receipt and
never repeat the mutation; reuse for a different request returns 409.
Security Model
- Setup routes require authenticated Filament/admin access.
- The OAuth callback validates state, PKCE, expiry, host, and exact redirect URI.
- OAuth, redirect, site, and control-plane URLs must use HTTPS outside localhost development.
- Refresh credentials, PKCE verifiers, and server signing credentials use Laravel encrypted casts.
- Server-to-server calls use short-lived signed requests from
tropikal-ai/connect. - Signatures cover method, path, normalized query string, timestamp, nonce, installation id, and body hash.
- Nonces are claimed atomically through the Laravel cache.
- Resource reads project declared fields only.
- Resource lists support pagination, text search, and exact filters over safe readable scalar fields only.
- Resource writes reject unknown or unsafe fields and return structured 400/422 responses for expected input problems.
- Mutations can require durable Website-side idempotency receipts, so a lost response cannot repeat an already committed operation.
- Staged image refs are installation/resource/field scoped, expiry bounded, one-time consumable, and decoded plus re-encoded before storage.
- Delete grants are explicit destructive capabilities and require confirmation.
- Eloquent discovery excludes secret-shaped fields before grants can be enabled.
- Write grants do not expose delete.
- Public browser payloads are checked recursively for secret-shaped keys.
- No secrets are returned to browsers.
See docs/security/threat-model.md for the release-candidate threat model.
Troubleshooting
403on/tropikal-connect/oauth/connect: the current user is not authenticated for the configured setup middleware.- Callback validation failed: the authorization server callback URL must exactly match
APP_URLplus the configured callback path, unlessCONNECT_FILAMENT_OAUTH_REDIRECT_BASE_URLis set. - Refresh status asks for a reconnect: the stored OAuth refresh credential expired or was revoked. Use the
Reconnectaction, complete authorization, and refresh again. - Signed API
401: the signature, query string, body hash, timestamp, nonce, or installation id did not match. - A discovered model is missing: check
included_model_namespaces,excluded_model_classes, and the model's table connection. - A field is missing from a business object: secret-shaped, hidden, guarded, relation, and unsafe fields are excluded by default.
- A write returns
422: the input did not match the explicit writable field schema.
Private Server Boundary
Server and control-plane internals are intentionally absent from this package. Public examples use example.com endpoints only.
Typed owner resource actions
An explicitly configured action may declare handler (a container-resolved class
implementing Contracts\OwnerResourceAction) and a closed input_schema. Its
handler receives validated arguments and returns the affected models of that
resource, at most200. Use this for domain operations such as reordering a selected
set; the handler still owns row locks, complete expected-revision checks and its
business rules. It must return every changed record for the site's audit.
The supported argument shapes are bounded scalar fields, lists with item schemas
(up to200 items), closed nested objects, and bounded maps with a property-name
pattern and value schema. Validation is strict about JSON scalar types, unknown
keys, bounds and duplicate list entries when uniqueItems is declared. Inputs are
limited to64KiB and nesting depth5. This reuses pure schema validation; public-chat
actor authorization is not involved.
Only installations granted action:<name> discover and execute the typed action.
The existing Connect dashboard has an Owner actions checkbox for each
declared typed handler. Each checkbox grants or revokes only that action; it
does not enable generic create, update, or delete. Read/field changes preserve
the action choices, and stopping resource sharing revokes them. Legacy model
method actions are not offered by these controls.
The catalog exposes its argument schema and always requires owner confirmation;
server handler names never leave the site. Execution uses the signed installation
route /resources/{resource}/actions/{action}, requires an idempotency key and
runs inside the existing durable mutation transaction. Responses and receipt
replay apply current per-field grants to every returned record. Existing
zero-argument record actions retain their original route and do not fall through
to this endpoint.
Handler validation failures use the controller's authored422 projection; uniqueness conflicts use its409 projection; unexpected failures use a generic500 projection even when debug is enabled. Exceptions unwind the durable transaction, so failed handlers do not retain database changes, audit rows or receipts.
Private photographic source staging
A field can opt into an application-owned OwnerSourceValidator and a bounded
asset.owner_actions list. Preparation then requires one of those exact declared,
trusted action grants; generic create/update grants do not substitute. Upload
rechecks connected installation/resource/field/action authorization, its bearer
capability, expiry, declared size and SHA. The trusted validator admits a private
local staged file without transforming it. Source bytes remain identical, including
photographic ICC/metadata, until the application-owned image lifecycle prepares
public derivatives. Private visibility, regular contained paths, full synchronized
staging writes, SHA verification and atomic rename are required. Validator failures
are redacted and unfinished temporary files are removed. A prepared row owns a
stable private source filename under its asset reference, guarded by a native
nonblocking lock. If the final database save fails, one verified original is
conservatively retained; retry reuses those identical bytes instead of proliferating
untracked UUID originals. Installation retention/reconciliation must include these
prepared and committed rows (and expiry/grace), not delete bytes inline after a
persistence failure. Private visibility is an adapter requirement; the installation
must also positively bind and verify its physical source root outside its public
server/static aliases. Existing fields without
this explicit contract retain the default image sanitizer.
This boundary does not expand the existing upload byte budget (5MiB by default),
make originals public, enable generic resource writes or replace application image
admission, generation, retention and publication policy. Larger CMS uploads use the
site's independently bounded owner workflow. Consume the staged reference through
StagedAssetManager::resolveForMutation inside the durable owner-action transaction;
its installation/resource/field/status/expiry checks still apply.