# Framework integrations

Each integration has a small, deliberate contract. Pass dependencies explicitly in
app constructors. Imports never register models, routes, jobs or templates.

## Ask your agent

```text
Read https://cicadel.fauzaanu.com/en/dev/topics/extensions/index.md.
Integrate [admin, storage or background-job feature] into this application.
Read the relevant integration API before writing code. Pass explicit dependencies,
keep authorization in the application, and test the real success and failure paths.
```

## Admin

`admin.Routes[T]` creates list, create, edit and delete HTML routes for a GORM
model with one integer primary key. You supply:

- an explicit field allowlist;
- a request-specific query scope;
- an authorization callback;
- a CSRF-token provider.

Permissions are required. They are checked both before and after applying
submitted fields. Unlisted fields cannot be assigned. Wrap the routes in session
and CSRF middleware.

> [!WARNING]
> Do not expose credential fields. The list is capped at 100 records in this
> initial version; it is not a general reporting interface.

### Run the catalog example

The catalog example shows related products and categories, with different editor
and manager permissions. It accepts Basic authentication over HTTPS using a bcrypt
hash from the environment. It is an integration example, not an
account-management product.

```sh
export CICADEL_CATALOG_PASSWORD_HASH="$(CICADEL_USER_PASSWORD='your local test passphrase' go run ./examples/catalog hashpassword)"
export DATABASE_URL='postgres://USER:PASSWORD@localhost/cicadel_catalog?sslmode=disable'
export CICADEL_DEBUG=true
export CICADEL_SECURE_COOKIES=false
go run ./examples/catalog migrate
go run ./examples/catalog runserver
```

- Sign in as `editor` or `manager` with the supplied password. Both can edit; only
  the manager can delete.
- The two roles share a demonstration password. Real projects should use their
  user directory and independently managed credentials.
- Send credentials from your HTTP client. Denied access returns 403, not a browser
  authentication prompt.
- `/admin/categories/` and `/admin/products/` host the admin. `/` is a paginated
  public product API.

## JSON APIs

| Helper | What it does |
| :--- | :--- |
| `api.Decode` | Requires JSON, rejects unknown fields and trailing values, and relies on the application's request-body limit. Validate required fields after decoding. |
| `api.Pagination` | Bounds limit and offset. |
| `api.Fail` | Returns consistent code, message and field errors. |

Use explicit output structs so model fields are never disclosed by accident.
Schema generation is not included. An optional schema layer can be added without
changing handlers.

## Mail

`mail.Sender` validates envelope and header inputs before calling a `Transport`.

- `mail.Render` uses separate text and escaped HTML engines.
- `mail.Capture` is concurrency-safe development storage.
- Built-in SMTP supports verified STARTTLS and implicit TLS, connection reuse for
  batches, MIME alternatives, attachments, and CC and BCC.
- Console, file, capture and discard backends support development and tests.

Configure the provider and verified destinations explicitly. See [email](https://cicadel.fauzaanu.com/en/dev/topics/email/index.md).

## File storage

`storage.Local` stores flat, application-generated keys in a rooted directory.
Writes are bounded. An object is replaced atomically only after the whole upload
succeeds.

`storage.HTTP` supports object storage through a signed-URL function you provide.

- It requires HTTPS, refuses redirects and bounds uploads.
- The trusted signer owns credentials, buckets and signature policy.
- It is a generic signed-URL adapter, not a bundled S3 credential or signing
  implementation.
- Test your provider's signing and required headers before deployment, and
  configure a client timeout.

Serve stored media with the right content types and authorization. Validate
filenames and content at the upload boundary.

## Cache

`cache.Memory` has a fixed entry capacity, a per-value byte limit, a key-length
limit, a TTL, copied values and explicit deletion. It is process-local. When full,
it evicts the oldest inserted entry.

- Use it only for results you can recompute.
- Invalidate keys after committed writes.
- Version key formats when output changes.
- Never treat cache presence as authorization.

Shared, multi-instance invalidation needs a different, application-provided cache
implementation.

## Jobs

Cicadel uses [River's PostgreSQL queue](https://riverqueue.com/docs) and exposes
River's own client and worker types.

| Function | Role |
| :--- | :--- |
| `jobs.Migrate` | Runs River's schema upgrades as an explicit deployment step. |
| `jobs.NewProducer` | Uses the application's SQL pool. |
| `jobs.InsertTx` | Requires an active GORM transaction and enqueues atomically with its writes. Call it inside `db.Transaction`, passing `tx`, not the outer `db`. |
| `jobs.NewWorker` | Uses a separate pgx pool, with explicit worker registrations and concurrency. |

Run a worker in three steps:

1. Start the worker once.
2. Call `Stop` with a shutdown deadline.
3. Close its pool.

River owns persistence, retry scheduling, job timeout configuration and queue
coordination. Set per-job attempt and timeout policies with River's options.

> [!WARNING]
> Workers must be idempotent. External side effects can happen again if
> completion acknowledgement fails. Transactional enqueueing does not make
> network calls atomic.

Prepared-statement transaction wrappers are not supported by the initial GORM
enqueue helper. The integration test proves that rolled-back jobs disappear and
committed jobs run. It does not claim exactly-once external delivery.

## Localization

Cicadel has no native internationalization or localization engine. The
experimental `locale` package was removed before release.

- Use third-party Go libraries through explicit middleware and template helpers.
- There is no bundled catalog, translation command, locale negotiation or
  humanization API.
- Browser translation does not remove server-side localization requirements.
- Accessible labels, validation errors and time-zone-correct timestamps remain
  application and framework concerns.

## Observability

`observability.Middleware` generates request IDs. It logs method, status, bytes
and duration, without bodies or query strings. It exposes a synchronous callback
for metrics or tracing adapters.

- Keep callbacks fast, concurrency-safe and non-panicking.
- Full distributed trace propagation is an application integration.
- Health handlers support optional, bounded readiness checks.

## Extension contracts

Public extension points are ordinary Go interfaces and functions:

- HTTP handlers and middleware;
- explicit app constructors and dependencies;
- `template.FuncMap` and filesystem sources;
- native GORM, SCS stores and River workers;
- mail transports, storage stores and observer callbacks.

Extensions share the core's version compatibility policy. There is no dynamic
plugin loading and no stable binary ABI.

### Installable apps

The [installable-app foundation](https://cicadel.fauzaanu.com/en/dev/topics/installable-apps/index.md) supplies manifests,
compatibility and dependency checks, namespaced commands, checks and resources,
PostgreSQL migrations, and startup and cleanup. Typed component contributions are
implemented.

Broader model and settings contracts and more module extraction remain open in
[the package acceptance checklist](https://cicadel.fauzaanu.com/en/dev/sources/batteries.md#package-system-acceptance-checklist).
Accounts, formsets, migrations and query generation already have independent
modules. Other integrations remain in the main module.

### Component packages

UI packages export typed `ui.Definition` contracts through `App.Components`, with
explicit children and named slots. See [the component UI design](https://cicadel.fauzaanu.com/en/dev/topics/component-ui/index.md).

- `template.FuncMap` and legacy template filesystems are compatibility-only.
- The starter and both application examples use compiled components.
- A completed component package must not depend on Django template syntax or
  hidden request context.

## Accounts and logging

The [accounts starter](https://cicadel.fauzaanu.com/en/dev/topics/authentication/index.md) uses compiled HTML and PostgreSQL cookie
sessions. The JSON bearer adapter remains available for API clients. Both use one
account service, shared revocation and explicit object authorization.
Administration remains a separate, limited integration.

[Logging](https://cicadel.fauzaanu.com/en/dev/topics/logging/index.md) provides:

- named configuration, propagation and filters;
- JSON and text handlers, and file rotation;
- optional captured or SMTP error alerts;
- credential redaction, request IDs and panic stacks;
- private GORM query timing.

Application configuration owns outputs and closes resources after requests drain.
