In development · not yet released
Compose the backend first. Write code only where you differ.
Compose a backend from dependencies. Replace any default with ordinary Go. Built for agents and people.Your application depends on capabilities, not implementations. Plystra Core composes them into a backend and lets you replace any default with ordinary Go. Built for coding agents first, and the same for people.
Dependencies
in go.mod
- plystra/authn
- plystra/authz
- plystra/data
- acme/platform
Capabilities
from dependency
- authn.password.login/v1authn
- authn.session.verify/v1authn
- authz.check/v1authz
- email.send/v1platform
- database.primarydata
- go.mod
- plystra.yaml
0lines of
custom Go
A running backend
composed, not written
Inspect composition
Dependencies in go.mod
- github.com/plystra/authn
- github.com/plystra/authz
- github.com/plystra/data
- github.com/acme/platform
Capability implementations
- authn.password.login/v1
- plystra/authn/password.New
- authn.session.verify/v1
- plystra/authn/session.New
- authz.check/v1
- plystra/authz/rbac.New
- email.send/v1
- acme/platform/smtp.New
- database.primary
- plystra/data/postgres
plystra.yaml
composition:
adopt:
- module: github.com/acme/platform
export: backendDependenciesin go.mod
- github.com/plystra/authn
- github.com/plystra/authz
- github.com/plystra/data
- github.com/acme/platform
Capabilitieswhat the backend needs
- authn.password.login/v1plystra/authn/password.New
- authn.session.verify/v1plystra/authn/session.New
- authz.check/v1plystra/authz/rbac.New
- email.send/v1acme/platform/smtp.New
- database.primaryplystra/data/postgres
Your repositoryall of it
- go.mod
- plystra.yaml
composition:
adopt:
- module: github.com/acme/platform
export: backend0lines of custom Go
A running backend
composed, not written
A backend needs capabilities such as password login, session verification, authorization checks, email and a database. Each is filled by an implementation that lives in a dependency listed in go.mod. The repository contains only go.mod and plystra.yaml, which adopts a reusable composition, and zero lines of custom Go.
Customize
A replacement is ordinary Go.
No plugin model to learn. Write a package with a constructor, as you would anyway.
package sesimport ( "context" sendv1 "github.com/acme/platform/interfaces/email/send/v1")type Config struct { Region string `yaml:"region" plystra:"required"`}type Sender struct{ region string }//plystra:implements email.send/v1func New(cfg Config) (*Sender, error) { return &Sender{region: cfg.Region}, nil}func (s *Sender) Send(ctx context.Context, req sendv1.Request) (sendv1.Response, error) { // Your behavior: send through Amazon SES. return sendv1.Response{}, nil}//plystra:implements is the only line Plystra reads. It is a Go comment.
What you don't write
- a base class to extend
- a registration call
- a plugin manifest
- container wiring
Replace
Every default can be replaced cleanly.
Add your implementation, then name it in one line. When two could fit, Plystra asks instead of guessing.
The default SMTP implementation of email.send/v1 is selected on its own because it is the only compatible implementation. When you add your own SES implementation there are two, and Plystra does not choose: plystra check stops with PLYSTRA_RESOLVE_MULTIPLE_IMPLEMENTATIONS. One line in plystra.yaml, written by plystra use, selects yours, and the check passes.
Agent-first
The same moment, as an Agent sees it.
Every command returns structured facts and a typed way forward. An Agent finishes the task and verifies it without knowing the framework's internals.
An Agent asked to send email through Amazon SES reads a small context: the Plystra guidance generated by plystra new, plystra.yaml and ses/sender.go. plystra check with JSON output returns status decision_required with a typed recovery of kind choose, listing each option with its exact command. The Agent picks the SES option, runs it, and the next check returns success.
Under the hood
Callers never see the swap.
Everything in front of an implementation is derived from the capability. Replacing it only rewires the adapter behind it.
A request from the browser goes through the JavaScript SDK, the Connect handler and the typed proxy, all derived from the capability, then through the Kernel and an adapter to the implementation. When the default SMTP implementation is replaced by your SES implementation, only the adapter is rewired; the SDK, handler, proxy and Kernel are unchanged, and the same request reaches your implementation. A call from another package in the same process enters at the proxy and takes the same path.
Repository
Your repository holds only what differs.
Generic capabilities stay in dependencies. Nothing is copied in by a scaffold, so nothing has to be merged later.
In a copied scaffold, generic authentication, authorization, database, HTTP and mail code sits in your repository next to your own. With Plystra those parts stay in dependencies. Your repository keeps go.mod, plystra.yaml and the one package where your backend differs.
Under the hood
Composed before the build.
The CLI resolves the whole composition, freezes it and derives everything else. No implementation is chosen at runtime.
Before the build, the CLI reads go.mod, plystra.yaml and your package, then discovers, normalizes, resolves and validates the composition and freezes it. After the freeze nothing can change it. It then generates the proxies, adapters, assembly, Connect handlers and JavaScript SDK into generated/, which is derived and never edited, and verifies the result. The Kernel runs it as one backend.
Official capabilities
Defaults, not requirements.
Data, AuthN and AuthZ are separate modules built the same way. Use them, or put your own in their place.
Data
github.com/plystra/data
Relational schema, queries and migrations, compiled before the build.
or yours — any implementation of the same Interfaces
AuthN
github.com/plystra/authn
Method-specific authentication, such as password login and sessions.
or yours — any implementation of the same Interfaces
AuthZ
github.com/plystra/authz
Authorization decisions, called explicitly where they are needed.
or yours — any implementation of the same Interfaces