11 Aug 2026 · 5 min read
Framework intelligence & discovery in Govard
How Govard detects your PHP and JS stack from filesystem signatures and boots the right services without a hardcoded framework switch.
Govard is a Go-native orchestrator for local PHP and web development. One of its core jobs is deciding, without any manual configuration, what kind of project sits in a directory and which services it needs. This post walks through the detection model: how a project is recognized, how frameworks register themselves, how forks relate to a parent framework, how you teach Govard about a bespoke stack, and how multiple projects talk to each other.
The design is documented in the architecture overview, and the extension contract lives in the adding a framework guide.
Detection signatures (composer.json, package.json, file-path)
When you run govard env up, the engine does not ask you which framework you are
using. It inspects the working tree and matches a set of detection
signatures. Each framework declares the signals it responds to, and the first
matching signature wins.
The most reliable signals are manifest files:
composer.json— for PHP projects. Presence of a package such asmagento/product-community-editionorlaravel/frameworknarrows the framework down immediately.package.json— for JavaScript-driven projects (frontends, PWA studios, build pipelines) that still need to be wired into the environment.- File-path signatures — when no manifest is conclusive, Govard falls back to
structural markers. A
bin/magentoentrypoint, aartisanscript, asymfony.lockfile, or awp-config.phpeach point to a specific framework.
This layered approach means detection works even on partial checkouts. A freshly
cloned repo with only composer.json resolves correctly; a full project with
both manifests resolves to the PHP framework and treats the JS layer as an
adjacent concern. The signatures are plain data on each framework definition,
not scattered if branches in the engine.
Tip: run
govard doctorafter a clone to confirm Govard recognized the framework you expected. A silent mismatch here is easier to fix before you boot the stack than after a container fails to start.
FrameworkDefinition & registry (no hardcoded switch)
The engine itself stays framework-agnostic. There is no central switch framework { case "magento": ... } anywhere in internal/engine/. Instead,
every framework ships a FrameworkDefinition that registers itself into a
framework registry at startup.
A FrameworkDefinition typically describes:
- The detection signatures from the previous section.
- The bootstrap routine that provisions services and mounts volumes.
- The blueprint assets (nginx, PHP-FPM, database init) the project needs.
- Defaults for credentials, ports, and tooling paths.
The registry is what the engine consults. When detection finds a match, the engine asks the registry for that framework's definition and delegates all framework-specific behavior to it. This is why adding support for a new stack never requires touching core dispatch code — you register a definition and the existing pipeline picks it up.
The adding a framework guide
explains the exact shape of a definition and where its blueprint assets belong.
The short version: framework-specific logic lives under
internal/frameworks/<name>/, while assets shared by several frameworks belong
in internal/blueprints/. Keep the definition self-contained and the engine
stays clean.
Forks inherit via Parent + FrameworkPatch (Mage-OS/Magento2, OpenMage/Magento1)
Real-world ecosystems fork. Magento 2 has the community-driven Mage-OS distribution; Magento 1 lives on as OpenMage. These are not separate frameworks that happen to look alike — they are forks that should inherit most of the parent's behavior and override only what diverged.
Govard models this with two building blocks:
- Parent — a framework definition can declare a parent. The child inherits the parent's detection signatures, bootstrap, and blueprints unless it says otherwise.
- FrameworkPatch — a scoped set of overrides. The child applies a patch to swap the package signature, adjust a service version, or replace one blueprint file. The parent definition is untouched.
So Mage-OS is expressed as a fork of Magento 2: it inherits the full Magento 2 service layout, then patches the composer signature and any distribution-specific paths. OpenMage is a fork of Magento 1 with the same inheritance relationship. Adding yet another Magento-family distribution becomes a thin definition plus a patch rather than a copy-paste of hundreds of lines.
This also keeps maintenance honest. When the parent fixes a bootstrap bug, every fork that inherits from it benefits automatically. Divergence is explicit and localized to the patch, which makes the registry easy to reason about.
Custom framework wizard (web server/db/cache/search/queue/varnish)
Not every project fits a packaged framework. You may be running a bespoke PHP application, a niche CMS, or an internal tool with its own conventions. For those cases Govard offers a custom framework wizard that lets you assemble a definition from the service primitives it already understands.
The wizard walks through the components your stack needs:
- Web server — nginx or Apache, with the docroot and vhost rules you specify.
- Database — MySQL/MariaDB (and the version you want), plus init and import hooks.
- Cache — Redis for sessions and backend cache.
- Search — OpenSearch/Elasticsearch when the application depends on it.
- Queue — a worker service for async jobs.
- Varnish — an edge cache layer in front of the web server for full-page caching.
Each selection maps to a service the engine already knows how to provision, so a
"custom" framework is really a composed definition rather than a from-scratch
build. The result is a reusable framework entry you can boot with the same
govard env up flow as a first-class framework. For one-off needs you can keep
it local; for something you repeat, promote it into a real
FrameworkDefinition following the adding a framework guide.
Inter-project connectivity (linked_projects)
Modern work rarely happens inside a single repo. A storefront might depend on a separate API project, or a frontend might need to call a backend running in another Govard environment. Govard handles this with linked projects: you declare that one project links to another, and Govard wires up the networking so they can reach each other by name.
Concretely, linked projects get resolvable hostnames on a shared internal
network. A frontend project can talk to backend.local without hard-coding IPs
or port-forwarding gymnastics. Because the registry already knows each project's
framework and exposed services, the link is just a connectivity declaration — the
underlying services are already defined.
This is also where the framework intelligence pays off indirectly: a linked Magento backend exposes its DB and cache the same way every Magento project does, so a sibling project can rely on stable service names. You spend your effort declaring relationships, not re-describing infrastructure.
Next in this series: Debugging & QA with Govard —
/blog/govard-debugging-qa