0.1.0-alpha.73

Documentation.

Install Formaku, publish your first page, and integrate the Commerce development package.

Install the starter

Use PHP 8.4+, Composer, Node.js 22.12+ and SQLite. Enable the PHP SQLite extension before running migrations. Clone the tagged alpha, then run the commands below. Run the administrator command by itself in an interactive terminal. Enter a password of at least 12 characters and wait for confirmation before running the demo command.

git clone --branch v0.1.0-alpha.73 --single-branch https://github.com/formaku/formaku.git
cd formaku
cd apps/starter
composer install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate
cd ../..
npm ci --ignore-scripts
npm run build
cd apps/starter
php artisan formaku:install
php artisan formaku:admin you@example.com
php artisan formaku:demo
php artisan serve

Open /formaku at the address printed by Artisan. Sign in with the administrator account you created. On a fresh installation, the demo command creates example collections and publishes /p/start. Create customer accounts separately.

For queued AI, scheduled publishing and notifications, run a worker and scheduler in separate terminals from apps/starter.

php artisan queue:work --tries=1 --timeout=90
php artisan schedule:work

Switch the panel language

Starting with alpha.16, use the panel header to choose Polski or English in Panel language (Język panelu). The browser remembers your choice. Collection names, custom field labels, and content keep the language in which they were entered.

Model data and publish pages

  1. In the builder, create a content or data collection. Give it stable field keys, define relationships, and configure workflow states and transitions where needed.
  2. Activate the definition. Use the generated list and editor to add a record. Content records can contain blocks such as text, images, cards and FAQs. Once saved, the record has a direct link to its editor.
  3. Save a draft and preview the page with an authorized panel account. Publish when it is ready. The live page continues to serve the last published revision while you edit the next draft.
  4. Use revision history to preview an earlier version or restore it to the draft. If the record has changed on the server, follow the steps to resolve a save conflict.

Export and import collection definitions through settings. Plan a Laravel migration before removing a field or changing its type when records already exist.

Add record and the collection picker in AI proposal review require permission to create records and edit all required fields in the collection.

Open a saved record in a content or data collection. The panel address includes the record ID, for example /formaku?record=123. Reload the page to reopen the record or select Open record in a new tab in the editor. To share the link with a teammate, copy the address from the browser.

The link opens the latest saved version. Anyone opening it needs to sign in to the panel and have access to the record. If the record is unavailable, you can still browse the collections you can access.

Resolve a draft save conflict

When a newer version of a content or data record is saved on the server, a conflicting save leaves your edits in the form. The editor pauses manual and automatic saves until you load the current draft.

  1. For a content record, use Preview in a new tab to compare your edits with the current saved draft.
  2. Choose Download my edits to keep a local JSON copy of the form values you can edit. Use the file as a reference when reapplying changes manually.
  3. Choose Load current draft and confirm that you want to discard the unsaved edits in the form. After the editor loads the server version, reapply the changes you want to keep and choose Save draft.

In a content or data collection, enter part of a title or name in Search by title or name and select Search. Formaku searches across all pages of the collection using the latest saved draft and the fields you can read. Characters such as % and _ are matched literally. Case and accent matching depend on your database. You can combine the search with Status filter and, in content collections, Publication filter. The search stays active when you change pages or return from the editor. To clear it, empty the search field and select Search again.

Filter records by workflow state

In a content or data collection with workflow states configured, open the Status filter above the record list and select a state. The selection stays active as you change pages and when you return from the editor. In a content collection, you can select Scheduled in Publication filter to find upcoming and overdue publications in that state. Select All statuses to clear the state filter.

Filter content by publication status

Open Publication filter above the record list in a content collection and choose one option.

  • Unpublished shows records with no currently published revision.
  • Published shows records with a published revision, including those with a newer draft.
  • With newer draft shows published records with a more recently saved draft.
  • Scheduled shows drafts with a saved publication time.

The record list uses Draft for a record with no currently published revision and Published for a record with a published revision. The Newer draft badge appears alongside Published when another draft is saved. The publication filter works with search and the workflow state filter. Select All publication statuses to clear the publication filter.

Schedule or cancel publication

Save the draft, choose Publication date and select Schedule publication. The editor shows the saved time in your browser’s time zone. Select Cancel scheduled publication to remove it and keep the draft. Saving another revision clears the earlier schedule. Keep the starter scheduler running to publish at the chosen time. On a Composer host, register the formaku:publish-scheduled command in Laravel’s scheduler.

A failed draft stays scheduled. The command processes the remaining drafts, then reports the number published, skipped, and failed and returns a nonzero exit code if any failed.

In a content collection, select Scheduled in Publication filter to see publication times from earliest to latest, including overdue drafts. Use the page controls to browse the results. Returning from the editor keeps the filter and the current page.

Unpublish a page

With permission to publish and edit the record, open the published record in the content editor, select Unpublish, and confirm. Formaku keeps the latest saved draft and the revision history and cancels any scheduled publication for the record.

The page disappears from the public catalog and sitemap. Its current and former public URLs return a 404 while it is unpublished unless another record uses the address. To make the page public again, open the record and publish the saved draft.

  1. In a relation field in the record editor, choose Wybierz rekordy (Choose records). Any records already linked in the field are selected when you open the picker.
  2. Use the checkboxes to choose one or more records. The picker shows up to 25 records per page and keeps your choices as you move between pages.
  3. Choose Zastosuj wybór (Apply selection), then save the draft. Choose Anuluj (Cancel) or press Esc to close the picker and keep the previous selection.

You can choose from records your account can access in the target collection. The picker omits private titles and names. Previously linked records stay selected if you lose access. Before saving, remove those links or regain access to the records.

Arrange blocks in containers and columns

In the page editor, add a container or a columns block. For a container, select Pionowo (Stack) to place blocks one below the other, or Automatycznie (Automatic) to let the grid fit the available space. For a columns block, select two or three columns. The grid uses fewer columns in narrow layouts and stacks blocks where space is tight.

Choose a block type inside the layout and select Dodaj blok do układu (Add block to layout). Use the up and down arrows to reorder blocks within that layout, and the remove button to delete a block from it. Save and preview the draft at different widths before publishing. Existing layouts saved without a setting keep their automatic grid.

For an image inside a container or columns block, choose a public image from the library in the same way as for a top-level block.

Reuse page templates

  1. As an administrator, save a draft with at least one block. Under Szablony stron (Page templates), enter a name and choose Zapisz jako szablon (Save as template).
  2. In the same collection, choose Dodaj rekord (Add record), then Pokaż szablony kolekcji (Show collection templates). Select a template and choose Wstaw sekcje szablonu (Insert template blocks) before adding any blocks to the new draft.
  3. Enter a title and URL, edit the copied blocks, and save the draft. Use Opublikuj treść (Publish content) when the page is ready.

You need permission to create records in the collection to use a template. The template copies only the blocks into the new draft. Editing that copy or deleting the template leaves the source page and other copies intact.

Preview the saved draft

Save the draft, then choose Pokaż podgląd (Show preview) in the editor. Switch between desktop (1280 px), tablet (768 px) and phone (390 px). The preview uses the public page renderer and the saved draft’s SEO title and description. Saving again refreshes the preview. Use Podgląd w nowej karcie (Preview in a new tab) to open it separately.

Choose Duplicate block to insert a copy after the selected block at the same level. Duplicating a container or columns also copies its nested blocks. Edit the copy independently and save the draft before publishing. Images in the copy refer to the same files in the media library.

Choose Move block to move a block to Page level, a container, or columns. The editor adds the block and its contents at the end of the selected list. You can then reorder it and save the draft.

Choose Undo block removal beside Page sections to restore the last block removed from the draft. The block keeps its nested content and media references. You can undo after autosave while editing the same draft.

Starting with alpha.27, edits to a saved draft during the refresh after publication are saved automatically when the refresh finishes. The edit stays in the draft until you publish again.

Browse revision history

Open a saved record and select Revision history. The editor shows 25 revisions per page, newest first. Use Next page to see older revisions and Previous page to return. Your unsaved edits stay in the editor while you browse.

In Revision history, select Compare with draft beside a revision to see how its fields differ from the saved draft. Formaku shows only changed fields you can read. Comparison uses saved values and keeps any unsaved edits in the form.

To restore a revision from any page of the history, save any pending edits, then select Restore to draft next to that revision.

Preview an earlier revision

In a content record, choose Preview revision next to any revision to open it in a new tab. You need access to the record in the panel. It uses the current collection definition and displays the fields you can read.

Publish the restored content draft when it is ready.

Manage the media library

Open Media in the admin panel. The library displays up to 30 files per page. If there is more than one page, use Poprzednia (Previous) and Następna (Next) to browse.

  1. Choose Edytuj metadane (Edit metadata) on the file.
  2. Edit Nazwa pliku (Filename) or, for an image, enter a description in Tekst alternatywny (Alternative text).
  3. Choose Zapisz metadane (Save metadata).

The updated filename appears in the library and in the file response's Content-Disposition header. The file URL and contents stay the same. Published pages that use the image pick up the updated alternative text.

Editing metadata for public files requires publishing permission because the changes affect public responses. In the starter, publishers and administrators have this permission. Staff with access to the work area can edit private file metadata.

Check where a file is used

In the media library, choose Użycie pliku (File usage) on a file. The paginated list shows the records you can access that use the file in a media field or an image block, including nested image blocks. It omits private titles and names.

The Szkic (Draft) and Publikacja (Published) badges identify which current revisions use the file. A record can have both badges. The list covers the current draft and published revisions only.

Choose a file for a record

  1. In a media field in the record editor, select Wybierz plik (Choose file).
  2. Find the file by name and select it. The picker lists up to 30 files per page, including images and PDFs, and shows whether each file is public or private. To close the picker and keep the current selection, choose Anuluj (Cancel) or press Esc.
  3. Save the draft to keep the selected file in the record.

File access follows the existing permissions. The picker for page image blocks lists public images only.

Choose an image for a page block

  1. Open an image block in the page editor and select Wybierz obraz (Choose image).
  2. Find an existing image by filename or thumbnail, then select it. The picker shows up to 30 public images per page and excludes private files and PDFs. To close it and keep your current selection, choose Anuluj (Cancel) or press Esc.
  3. Enter an optional caption in Podpis obrazu (Image caption). Save and preview the draft before publishing.

Serve responsive WebP images

Formaku can create 480 px and 960 px WebP variants of public JPG, PNG, and WebP images without upscaling. The page uses the variants when available and falls back to the original image at its existing URL. The caption and alternative text are preserved.

Formaku processes eligible new public uploads. For existing images, run php artisan formaku:media-variants in the Laravel application. If the Docker Compose starter is running, execute the command in the web service.

docker compose --env-file .env.docker exec web php artisan formaku:media-variants

The Docker release includes GD with WebP support and EXIF. On your own Laravel host, enable GD with WebP support and enable EXIF for JPEG processing. Formaku serves the original whenever a variant is unavailable.

Link page translations

  1. Save each language version as a separate page in the same public content collection.
  2. On each page, open Language versions. Enter the Page language, such as pl or en-GB, and use the same Translation group key, such as about. Select Save translation link.
  3. Publish each page. Once at least two are public, visitors can switch between them. Formaku adds reciprocal hreflang links using the public base URL from APP_URL.

You need permission to edit the record and publish pages. Link changes take effect immediately on published pages. To unlink a page, clear both fields and save the link.

Configure SEO and the sitemap

In the panel, open Buduj (Build), choose Edytuj model (Edit model) for a public content collection, and use Metadane SEO (SEO metadata) to map the optional title and description to text fields that are visible publicly. Save the collection definition, fill in the fields on a page, and publish its revision. The live metadata stays tied to that published revision while you edit a draft.

With an empty SEO title, Formaku uses the page title. With an empty SEO description, it omits the description tag.

The starter serves /sitemap.xml with the home page and published pages from public content collections. Drafts stay out of the sitemap. Set APP_URL to the public base URL, including any subdirectory. On larger sites this route returns an index of sitemap files.

Set FORMAKU_SITEMAP=false when the host application serves its own sitemap. When integrating the CMS package directly, enable public_routes and sitemap in config/formaku.php to use the Formaku sitemap.

Keep internal fields private

Starting with alpha.33, public pages, the catalog and cards respect the model’s Private field setting. Private blocks remain in the saved draft and editor preview. When a title is private, Formaku displays a public name or the URL slug. Set file visibility separately in the media library.

In Build → Edit model, clear Private for slug when the addresses may be public. Turn off Public content collection when the addresses must stay private. The upgrade hides existing collections with a private slug while retaining their records and revisions.

Choose roles in the model editor

In Build → Edit model, configure collection access and add roles from your application’s catalog or enter a role key. A new rule starts with no access. Choose the record scope and write permission before saving. With an empty rule set, access is limited to users with Build permission. Select Restore default access and save to restore the default.

You can also select catalog roles for field permissions. The host Laravel application assigns roles. See role catalog configuration.

Set field permissions by role

Open Build → Edit model → Field permissions. Enable Restrict reading or Restrict writing for a field and choose roles from the catalog or enter their keys separated by commas. An enabled restriction with an empty list allows only users with Build access.

Read restrictions apply to private fields other than slug. The editor and history omit unreadable values. Title search is unavailable when the model hides a title or name field from your role. Draft saves and restores preserve current values the user cannot edit.

Templates require write access to page blocks. Collections with field rules are unavailable to the paired assistant. File usage omits collections that contain media-reference fields the user cannot read. See the configuration and rollback notes for Laravel hosts.

Keep links working after changing a page URL

Change the slug of a published page in a public collection and publish the new revision. The previous URL returns a 301 redirect directly to the current URL, even after further changes. A published page that reuses an old slug takes precedence at that URL. Hiding a collection also hides redirects to its pages.

Formaku records URL changes made after installing alpha.4 or a later version. Before updating an existing installation, follow the backup and migration instructions.

Set up the customer portal

In the starter, enable registration only if customers should create their own accounts. Configure mail and email verification before relying on registration or password reset. Customers submit and view their own requests at /portal. Staff can assign a request to a team member when updating its status.

The public catalog and customer portal show previous and next links on narrow screens. Wider screens show numbered pages and the result count.

In request history, select a state and click Filtruj (Filter). Active filters stay in place when you change pages. Use Wyczyść filtry (Clear filters) to return to the full list of your requests.

Enter the exact number in Numer zgłoszenia (Request number) to find one of your requests. The number and state filters apply together. Results show the subject when you have permission to read it.

Add fields to the request form

In Build → Edit model, open the requests data collection and choose fields under Customer portal fields. The form always includes Subject and Description. You can add text, numbers, dates, yes/no fields and select lists. Save the model to update /portal.

Under Form field order, use Move up or Move down next to a selected field, then save the model. Subject and Description come first. The form and request details display the additional fields in the saved order, subject to the customer’s permissions.

Additional fields follow the model’s required setting and the customer’s permissions. Customers see saved values they can read in their own request details. Keep internal fields private.

Allow customers to attach files

In the requests model, enable Request attachments. Choose a limit of one to three files, a maximum size of one to five MiB per file, and at least one allowed type from PDF, PNG and JPEG. Save the model. The portal displays and enforces these limits when a customer submits a request. By default, all three types are allowed with up to three files of up to 5 MiB each.

The request owner and staff with record access can download saved files from the request details. Changes to the upload limits apply to new files.

Turn off Request attachments to stop new uploads. Existing files remain available according to request permissions. On a Laravel host, follow the private storage and upload limit instructions.

Use collection rules to scope editor and publisher access to all, owned, or assigned records, or to deny access. Configure write access separately. When collection rules are enabled, roles without an entry are denied access. Mark internal fields as private to keep them out of portal responses.

Integrate with Laravel

In an existing Laravel 13 application with PHP 8.4 or newer, add the Formaku Composer repository and install the release. The package includes the compiled panel.

composer config repositories.formaku composer https://formaku.eu/composer
composer require 'formaku/cms:0.1.0-alpha.73' --prefer-dist
php artisan formaku:install
php artisan migrate

Configure the host session guard and authorization callback in config/formaku.php. Sign in through your Laravel app with an account that has work access, then open /formaku.

Administrative access is denied by default and public routes are disabled until enabled. The package uses your application's authentication. Use the starter if you also need its login and customer portal setup.

Use the developer examples to register a custom block and call a resource and action from authenticated routes in your Laravel application.

Check the installation

Run php artisan formaku:doctor from your Laravel app. The read-only check verifies Formaku migrations, panel authorization configuration, and the published JavaScript and CSS. If a check fails, follow the developer guide.

Install from a release ZIP

Download the Composer ZIP (v0.1.0-alpha.73) and its SHA-256 checksum. Check the archive before installing it. Follow the local ZIP instructions to register the package in Composer.

Before a new release is published, GitHub Actions installs its ZIP in a separate Laravel 13 app with SQLite. The check verifies that Formaku can run its migrations and publish the panel assets while the app retains its own route and a test record.

Build the CMS stylesheet

The bundled stylesheet is built from the Formaku admin components, Blade views and renderer. Run npm run build from the repository root. If a Laravel extension needs additional utilities, compile its styles in the host app. See the developer guide.

Upgrade an existing Composer installation

After updating to v0.1.0-alpha.73, run these commands in the host Laravel application before serving traffic.

php artisan formaku:install
php artisan migrate --force

Alpha.61 adds a revision history index for PostgreSQL and SQLite. Apply the migration before resuming traffic. PostgreSQL blocks writes to the revisions table during index creation. Existing drafts, published content and revisions stay in place.

Before serving traffic, run php artisan formaku:doctor to confirm the migrations, panel access configuration, and deployed CSS and JavaScript.

Starting with alpha.31, formaku:install removes cached copies of the package Blade templates. Compiled host views stay in place. If your deployment precompiles views, run php artisan view:cache afterward. If you are installing alpha.30, run php artisan view:clear after the update.

Register custom field and block types

In your Laravel provider, register a field type with Registry::field(). Open Build → Edit model and select the extension's label in the type list. In the record form you can edit text, rich text, numbers, dates, yes/no values and lists with options. Fields whose type has no supported control appear as read-only. For example, a price field can accept whole cents and reject negative values.

Register a PanelBlockType with Registry::block(). In the page editor, choose its label under Page sections and add the block. The editor displays text, multiline text, number, yes/no and choice settings declared in panelSchema(). If the block accepts children, add or move blocks into it before saving and previewing the draft.

Settings without a supported control and settings on older blocks without metadata stay in the draft. Use the API or an editor in your Laravel app to change those values. See the block settings example and the field type example.

Show Laravel resources and actions in the panel

Register a resource or action with explicit panel configuration in your Laravel application. Define who can use the extension and select the table columns or form fields. Authorized users can then open Extensions in Formaku.

The example provider shows the signed-in user’s profile and runs a notice preview. Follow the developer setup instructions to try it on a Laravel host.

Review an AI page proposal

For optional AI, configure a provider and model on the server using OPENAI_API_KEY / OPENAI_MODEL or GEMINI_API_KEY / GEMINI_MODEL. With a queue worker running, generate a page proposal and refresh the task list.

To limit output tokens per proposal request, set OPENAI_MAX_OUTPUT_TOKENS or GEMINI_MAX_OUTPUT_TOKENS on the server. Each defaults to 3000. Use an integer from 16 to 3000. Rebuild the configuration cache if used and restart queue workers after changing the settings.

Formaku validates the ordered list of sections before marking the proposal ready. If generation fails, check the message on the task card before submitting a new request.

When the task is ready, select Review proposal and choose a collection where you can create pages and edit the title and sections. Check the title, proposed URL if the collection supports it, and the content and order of the sections. Apply as new draft opens the editor with those values. Select Save draft to create the record. Publish it separately from the editor.

The review lists editable required fields that will stay empty when you apply the proposal. The list updates when you choose a collection. Fill those fields in the editor before saving the draft.

Pair a website assistant

Pair Codex to create and edit page drafts in one selected public content collection. The collection must have no private fields or relationships.

  1. Open Asystent witryny (Website assistant) in the admin panel and select the collection.
  2. Download the MCP adapter and generate a pairing code. With Node.js 20 or newer, run the command shown in the panel from the download folder and enter the code in the terminal.
  3. Restart Codex and ask it to create or edit a draft in the selected collection.

The code works once and expires after five minutes. The connection lasts 30 days. Pairing grants draft access by default. Enable publishing separately during pairing if you want the assistant to publish on your request. Use Cofnij dostęp (Revoke access) in the panel to end the connection.

Pair another MCP client

Starting with alpha.17, for an MCP client that supports stdio, append --print-config to the pairing command from the panel. After entering the one-time code, copy the generated JSON mcpServers entry into the client configuration. If the client uses another format, copy the command and args values. Restart the client. The token stays in the local connection file.

Update an already paired adapter

Starting with CMS alpha.20, download the latest adapter from the panel. If your pairing is still active, run this command from the download folder using the same full address shown in the panel’s pairing command.

node formaku-assistant.mjs update ADDRESS_FROM_PANEL

Restart your MCP client to load the new tools. The update replaces the local adapter and preserves the existing token and client configuration. Use the pairing instructions above if the connection has expired, been revoked, or needs to use a different address.

Check and preview a draft

Starting with alpha.17, use the current MCP adapter to ask Codex to check the collection fields and validate a draft made of built-in blocks before saving. Validation leaves the draft unsaved. After saving, ask for the draft preview link and open it in a browser where you are signed in to Formaku with access to that draft.

Codex can reuse blocks from a saved template in the paired collection.

Compare a saved draft with its publication

Update the MCP adapter and ask the paired assistant to compare a page’s saved draft with its current published revision. The assistant reports the changed fields and the record version. For a page awaiting its first publication, it shows the draft values.

If the connection has publishing permission, you can ask the assistant to publish the reviewed version. If someone changes the draft in the meantime, review the changes again before asking it to publish.

Reuse a published image in a draft

Starting with alpha.18, ask the assistant to list public images used in the current published versions of pages in the paired collection. The assistant can insert the selected image ID into an image block in a draft. The list skips private files and images used only in drafts, older revisions, or other collections.

When validating and saving drafts, the assistant also checks nested block lists. If a list contains more than 100 blocks or nesting exceeds the 10-level limit, shorten or simplify the layout before saving again.

Read product variants from Commerce

Starting with CMS alpha.19, ask the paired assistant to list products for a draft. It uses the public Commerce v2 catalog when the installation exposes it. Each entry identifies the product and the variant separately. The variant can have its own label, such as a size, and the entry includes its price and whether it is in stock.

If the installation exposes only the v1 catalog, the assistant continues to read products from v1. It reads either public catalog without sending the CMS token.

Starting with CMS alpha.20, if the public Commerce v2 catalog exposes categories, ask the assistant to list published categories and filter the variants by a category’s slug. For example, ask it to use only variants from the coffee category in an offer draft.

Starting with CMS alpha.21, if a compatible Commerce storefront returns a public product path in public_path, ask the assistant to use it for the product button in your draft. The adapter accepts only paths on the same website. If the catalog provides no usable product path, keep the existing link to the catalog.

Prepare a CMS deployment

To build an image from the panel, open Releases and enter the full SHA of a commit on main. Select Refresh status in the build row to read its GitHub Actions status. When the build succeeds, open its run and copy the image digest from the summary. Deployment is a separate workflow.

Check the result of a deployment attempt

In Releases → Deployment receipt, enter the numeric GitHub Actions run ID, the deployment UUID and the full image reference including its digest. Select Check receipt to read the report for the latest attempt of Deploy existing image on main.

The report shows the SSH exit code and the state the host reported after that attempt, including rollback details when available. If the report or host status is unavailable, open the workflow summary and use the controller’s status command for a fresh reading. The CMS host needs PHP’s ZIP extension and a GitHub token with Actions read permission.

Read the controller’s latest record

Run Read deployment status on main with the deployment UUID and full image reference including its digest. The workflow reads the controller without starting a deployment. The summary shows the most recent state recorded for that attempt. If a newer attempt replaced the record, the workflow reports that the status is unavailable.

Verify the running image

On the deployment host, run sudo /usr/local/sbin/formaku-deployctl runtime with the updated controller. The command compares the recorded image with the healthy running containers for the web process, queue worker and scheduler. The verified result includes the running image digest, even after a rollback.

transitioning indicates an active or changing deployment, mismatch indicates a discrepancy, and unavailable means the controller lacks the evidence to verify the runtime. Check the public route separately. See the controller guide.

Measure Formaku performance

The reference benchmark measures a public page and the authenticated record list on synthetic data. It uses native Linux AMD64 Docker with per-service limits totaling 2 vCPU and 4 GiB. Four clients measure each endpoint separately after warmup. The report includes request counts, errors, throughput and p50, p95 and p99 latency.

Use a published image digest and follow the benchmark procedure to reproduce the measurements.

Three alpha.65 samples completed 7,976 requests without errors. Across the three samples, p95 was 94.6–97.3 ms for the public page and 122.0–144.4 ms for the authenticated record list. See the run summary and JSON reports.

For a deployment, configure PostgreSQL 17 or MySQL 8.4, set a persistent APP_KEY and a database password, and run the web process, worker and scheduler. The Docker Compose starter uses the same image for all three processes.

Configure an HTTPS reverse proxy, verify backups and restoration, and check that the portal and publishing workflows operate before opening the application to users.

Before upgrading an existing installation, back up the database and uploads. Before starting the updated services, run php artisan migrate --force in the new version. Alpha.49 adds no migrations. Alpha.37 adds the request attachments table. Back up the database together with private uploads. Alpha.30 adds a table for page translation links and preserves existing drafts and publications. Alpha.15 added a table for collection page templates. If upgrading from alpha.12 or earlier, the same command applies the navigation and footer settings migration from alpha.13. When upgrading from alpha.10 or earlier, the alpha.11 migration creates the file usage index and fills it from the current draft and published revisions. The command also applies any earlier pending migrations, including the redirect table added in alpha.4 and the assistant pairing tables for upgrades from the first alpha.

For a Composer installation, follow the Laravel upgrade steps before serving traffic.

Follow the Docker Compose guide to build the tagged source. You can also pull the public Linux AMD64 image by digest. Compose checks the worker and scheduler separately.

docker pull ghcr.io/formaku/formaku@sha256:2860f35e300fd76e6d2f8256faa64b8e32814135924a67a1dfd4f34c403d3bcc

Verify the image source and revision

Starting with alpha.39, the release workflow checks three OCI labels on the published Linux AMD64 image by digest. org.opencontainers.image.source identifies the Formaku repository, org.opencontainers.image.revision gives the full commit SHA, and org.opencontainers.image.version gives the release version or the source SHA for a manual build. The deployment guide shows how to read the labels with Docker.

Test a published image

Open CI → Run workflow on GitHub and paste the release digest. The test pulls that image, runs the installation check, and starts the Compose services with a fresh database. Use a release from alpha.48 or later. See the GitHub Actions instructions.

Check deployment and rollback status

On a host using the Formaku deployment controller, run sudo /usr/local/sbin/formaku-deployctl status. A failed deployment reports whether Compose restored the previous image, could not confirm its health, had no previous image, or failed before application services were stopped. A backup entry identifies the completed encrypted archive. Use a fresh release UUID for another attempt.

Image rollback keeps the existing database and files. Follow the deployment guide for status values and data recovery.

Read the deployment result in GitHub Actions

After an alpha.41 deployment run, open its summary for the status of the requested release and image. A matched failed deployment can show the rollback outcome and completed backup UUID. The workflow keeps the deployment command’s original result.

To enable the status lookup, update the host controller and SSH forced command and add the read-only rule in the host setup instructions.

These instructions describe the Formaku v0.1.0-alpha.73 prerelease. Use the CMS repository to follow source changes and report issues.

Formaku Commerce

Commerce is an optional API package for Formaku and other Laravel applications. The development version supports one store in one currency, an SKU catalog, signed-in checkout, pickup, and manual bank transfer confirmation. Use a test environment and synthetic data for evaluation.

Install in a Laravel application

You need PHP 8.4+, Laravel 13.17 or later within 13.x, and a copy of the package source. Run these commands in the host application. Replace the example path with the package's local directory. Packagist installation is planned after release.

composer config repositories.formaku-commerce path "/absolute/path/to/packages/commerce"
composer require formaku/commerce:@dev
php artisan vendor:publish --tag=formaku-commerce-config
php artisan migrate
  1. In config/formaku-commerce.php, set the currency before adding products and choose a guard if the application uses a custom one. Enter amounts in minor units. For example, 4990 is PLN 49.90.
  2. Define Laravel Gate abilities formaku-commerce.catalog, formaku-commerce.stock, formaku-commerce.orders, and formaku-commerce.payments in the host application. Grant payment confirmation separately.
  3. Set FORMAKU_COMMERCE_ENABLED=true. Rebuild configuration and route caches if the application uses them. Run the Laravel scheduler, which invokes formaku:commerce-expire every minute.

Test the order flow

The default API prefix is /formaku/api/v1/commerce. Send Accept: application/json. The catalog is public. All other routes use the Laravel session, and requests that change data require CSRF protection.

  1. Create a published product with POST /admin/products. Supply sku, name, price_minor, available, and published: true. Check that it appears in GET /products.
  2. Sign in as a customer. Send POST /quotes with a list of items containing product_id and quantity. Pass the quote ID as quote_id to POST /orders. The customer can view their order with GET /orders/{id}.
  3. With payment permission, send POST /admin/orders/{id}/pay with reference, amount_minor, and currency. After pickup, call POST /admin/orders/{id}/complete.
  4. On a separate order, check cancellation with POST /admin/orders/{id}/cancel and check stock release after a reservation expires.

A quote is valid for 10 minutes. If prices change or stock runs out, the API returns 409. Request a fresh quote. Retrying checkout with the same quote_id returns the existing order. Reservations expire after 30 minutes by default. Reconcile late transfers manually.

To adjust stock, use POST /admin/products/{id}/stock with delta and expected_available. On conflict, read the current stock before retrying the adjustment.

Before production use, verify permissions, concurrent purchases of the last item, and reservation expiry on your target database. A ready-made storefront and commerce admin UI, variants, taxes, shipping, online payments, and returns are outside this version's scope.