← All projects
Application framework + tooling

uBixCore

Active PHP 8.4+ · React 19 · Slim 4 · PHP-DI

A framework you install, not a repo you fork. The PHP core, the React component library and the whole quality gate arrive through Composer and npm; your project keeps its own namespace and its own git history, and uBixVault holds its secrets.

Packages
ubixsys/ubixcore · @ubixsys/ubixcore · ubixsys/ubixcore-skeleton
License
BSD-3-Clause
Shape
Framework + skeleton (Laravel / Symfony model)
Secrets
Resolved from uBixVault
Synopsis
shell
$ composer create-project ubixsys/ubixcore-skeleton acme
$ cd acme && APP_NAME=HelloApi php -S 127.0.0.1:8080 -t public
$ curl 127.0.0.1:8080/health   # {"status":"ok","app":"HelloApi"}
$ bin/ubix code:review            # phpcs · phpstan (level max) · phpunit
Description

uBixCore is two things published together from one tree: a framework (ubixsys/ubixcore on Composer, @ubixsys/ubixcore on npm) and a skeleton (ubixsys/ubixcore-skeleton) that composer create-project turns into a working host project. The framework lives in vendor/ and is never committed to your repository; your committed composer.lock is what pins the version every image is built from.

The point of the split is that the tooling ships with the framework, not just the library: the bin/ubix CLI, the Ubix phpcs standard with its custom sniffs, a PHPStan baseline at level max, PHPUnit base classes that enforce the house rules on every class, the migration runner, and the Docker and GitLab CI templates. A new project starts with the whole workflow.

Ubix\ is the framework's namespace; you never write code there. Your project has its own PSR-4 root and the framework finds your controllers, commands and tests through Composer's map. Secrets are read from uBixVault at startup; non-secret configuration is environment on the Deployment. The image ships no .env, on purpose.

Examples
an endpoint in your namespace
// php/Acme/Controller/HelloController.php
namespace Acme\Controller;
use Ubix\Controller\AbstractController as Controller;
final class HelloController extends Controller {
    public function hello(Request $request, Response $response): Response {
        return $this->renderJson($response, ['hello' => 'world']);
    }
}
// app/AcmeApi/src/Routes.php
$app->map(['GET'], '/hello', HelloController::class . ':hello');
the quality gate
$ bin/ubix code:review          # same three tools CI runs, same rules from vendor/
$ vendor/bin/phpcs              # <rule ref="Ubix"/> — 230+ sniffs
$ vendor/bin/phpstan analyse    # includes the framework's level-max baseline
schema migrations
$ bin/ubix migrate:diff          # live schema vs checked-in reference
$ bin/ubix migrate:up            # apply in filename order, destructive-change guard
$ bin/ubix migrate:status
upgrade the framework
$ composer update ubixsys/ubixcore
$ git add composer.lock && git commit -m "chore: ubixcore 0.2.0"   # one-line diff, one MR
Capabilities
Install, don't fork
The framework is a Composer dependency in vendor/. Nothing of it is committed; the lock file records which version built each image.
Your namespace
Ubix\ is reserved for the framework. Controllers, models, data types and commands live under your own root — Acme\, Kitg\SowingMe\ — and the house rules apply to them by family, not by vendor prefix.
Tooling ships with it
CLI, coding standard, PHPStan baseline, PHPUnit base classes, migration runner, Dockerfiles and pipeline all arrive with the package. docs/ci-setup.md lists every variable and secret a first green pipeline needs.
Command-discovering CLI
bin/ubix finds commands through Composer's PSR-4 map; add your namespace to one array and your commands appear beside the framework's.
Machine-enforced standard
The Ubix phpcs standard with custom sniffs, PHPStan at level max, strict PHPUnit, and a test base that checks every class against the house rules — and proves each concrete class has a test.
Typed contracts
Interface-bound repositories over DTO options, custom data types for domain values, controllers speaking in typed payloads — Slim 4 and PHP-DI underneath, Latte for templates.
Versioned migrations
Timestamped SQL, a central tracker, diff against a reference dump, and a destructive-statement guard with a backup before it applies.
uBixVault first
Database credentials are resolved from uBixVault at boot over Kubernetes auth; CI reads its secrets with read-only tokens. No .env in an image, ever.
One tag, three packages
A single v* tag publishes the PHP framework, the React library and the skeleton with matching version numbers.
React 19 library
@ubixsys/ubixcore: the shared component library, in TypeScript, for React Router apps built on the framework.
Installation

Two scenarios: the first ten minutes, and the first afternoon.

A developer needs a running app; an operator needs a green pipeline. They are different jobs, so they get different walkthroughs.

Scenario 1 · developer quickstart

Requirements: PHP 8.4+, Composer 2, git. Node 22+ only if you add a React app.

1 · create
$ composer create-project ubixsys/ubixcore-skeleton acme
$ cd acme
# thin bin/ubix + public/index.php, a HelloApi app, php/App/ (your PSR-4 root),
# phpcs/phpstan/phpunit configs pointing at vendor/, Dockerfile, pipeline, .env.example
2 · git
$ git init -b dev && git add -A && git commit -m "chore: bootstrap from ubixsys/ubixcore-skeleton"
# composer.lock is committed on purpose; vendor/, node_modules/ and .env* are not
3 · run
$ php bin/ubix list                        # the framework's commands, plus yours
$ APP_NAME=HelloApi php -S 127.0.0.1:8080 -t public
$ curl 127.0.0.1:8080/health
4 · make it yours
# rename the namespace: composer.json autoload App\ -> Acme\, php/App -> php/Acme,
# then the five files that say App\ (composer dump-autoload afterwards)
$ bin/ubix code:review                     # green before your first commit
Scenario 2 · self-hosted GitLab + k3s + uBixVault

Assumed stack: GitLab with its container and package registries, a shell runner with Docker buildx and kubeconfigs, k3s namespaces per tier (ws-<env> for APIs, live-<env> for web), one uBixVault per tier with Kubernetes auth. Substitute your hosts; the shape is the same. Every step is spelled out in the skeleton's docs/ci-setup.md.

  1. Create the project from the skeleton and push it. The pipeline is included: build → lint-and-test → deploy → promote → notify.
  2. Registry access. The image installs the framework at build time. Either store a read-only deploy token in uBixVault at secret/<project>/composer, or add your project to the framework's job-token allowlist. The auth enters the build as a BuildKit secret, never a layer.
  3. CI variables, masked: UBIXVAULT_CI_TOKEN_DEV, UBIXVAULT_CI_TOKEN_PROD, GITLAB_PROMOTE_TOKEN.
  4. Seed uBixVault with bin/vault-ci-setup.sh <env> <project>, admin token in the environment. It creates the read-only policy, writes test-db, composer and discord, and prints the CI token. Pod credentials live at secret/<app>/<env>/db, read through VAULT_ADDR + VAULT_K8S_ROLE.
  5. Namespaces: regcred, the wildcard-TLS label, and the non-secret runtime config (MEMCACHE_SERVERS, database host/port/name, log and template paths) as env on each Deployment.
  6. Push to dev and read the first failure against the list at the end of docs/ci-setup.md; each maps to a step above.

Lesson learned moving a real product onto this: a baked .env hides every setting an image depends on. Put secrets in uBixVault and the rest on the Deployment, and the image can be promoted between tiers unchanged.

Reference

Packages, layout, commands.

What you get, where it goes, and what you type.

Packages · one tag publishes all three
PackageRegistryNotes
ubixsys/ubixcoreComposerThe PHP framework: Ubix\ namespace, CLI, standard, test bases, migrations. Source on GitHub.
ubixsys/ubixcore-skeletonComposerThe create-project template. Source on GitHub.
@ubixsys/ubixcorenpmThe React 19 + TypeScript component library for React Router apps.
ghcr.io/ubixsys/ubixcore-phpcontainerThe PHP runtime image: nginx + PHP-FPM + memcache(d) on Alpine, multi-arch, tags 8.5 and 8.4. Source on GitHub.
Host project layout · what the skeleton gives you
PathOwnerNotes
composer.json / .lockyouRequires ubixsys/ubixcore; the lock pins the framework your images are built from.
bin/ubix · public/index.phpyouThin entry points that call Ubix\Bootstrap; the only values in them are yours (command namespaces, app name).
app/<App>/src/youDependencies.php (PHP-DI), Middleware.php, Routes.php, plus the app's Kubernetes manifests. One folder per deployable app; APP_NAME selects it per image.
php/<Vendor>/youYour PSR-4 root. Families (Controller, Model, Repository, DataType…) get the house rules regardless of the prefix.
phpcs.xml · phpstan.neon · phpunit.xmlyouPoint at the rules that ship in vendor/ubixsys/ubixcore; add your own overrides below them.
vendor/ubixsys/ubixcoreframeworkNever committed. Upgrade with composer update ubixsys/ubixcore.
bin/ubix · command groups
CommandGroupNotes
code:reviewCodephpcs, phpstan and phpunit as one gate, the same locally, in the pre-push hook and in CI. Also code:commit, code:merge, code:loc.
migrate:up · status · diff · reconcileMigrateApply pending migrations, list state, diff the live schema against the reference dump, or record one as applied without running it.
app:run · app:build · app:deployAppRun an app locally, build its image, apply its manifests.
database:*DatabaseSchema reset and bootstrap helpers for local and test databases.
cron:*CronScheduled jobs, loaded only when a cron command is invoked.
<yours>anyExtend Ubix\Console\Command\AbstractCommand under your namespace; bin/ubix discovers it.
Architecture

The framework asks; the host answers.

Nothing in vendor/ knows where your project is or what it is called. The bootstrap tells it, once.

The stack
bin/ubix · public/index.php
your thin entry points → Ubix\Bootstrap
Slim 4 HTTP · PHP-DI · Symfony Console
app/<App>/src/{Dependencies,Middleware,Routes}.php
Your controllers → services
typed payloads · your namespace · house rules by family
Repositories · custom data types
interface-bound · DTO options · framework bases
MySQL / MariaDB · uBixVault
versioned migrations · credentials resolved at boot
How it fits

Your bin/ubix and public/index.php are a dozen lines each: they call Ubix\Bootstrap\environment(), then console() or http(). The bootstrap exports the project root, loads secrets from uBixVault, and builds the Symfony Console or Slim app from your app/<App>/src files.

Inside the framework, one service resolves every path from that root, so nothing computes locations from its own file. Commands are discovered through Composer's PSR-4 map, which is why a host namespace works with a one-line change.

The coding standard, PHPStan baseline and PHPUnit base classes are part of the package. A host's phpcs.xml is <rule ref="Ubix"/> plus its file list; the test base decides which rules apply to a class from its family segment, so a Kitg\SowingMe\Controller\Api\X is held to the same controller rules as anything in Ubix\Controller.