Configuration reference
A TSG deployment is described by one YAML document. This page is the reference
for apiVersion: tsg.dataspac.es/v1alpha2; run tsg --version to see which
versions your CLI accepts.
apiVersion: tsg.dataspac.es/v1alpha2
kind: TsgDeployment
metadata:
name: alfa
spec:
namespace: tsg-alfa
dataspace: { ... }
admin: { ... }
stacks: [ ... ]
The CLI rejects unknown fields. Otherwise, a renamed field could be ignored and produce the wrong manifests without an error.
Core concepts
These terms distinguish the deployment, its runtimes, and the identities hosted by those runtimes.
| Concept | What it is |
|---|---|
spec.dataspace | The trust domain the stacks in this file join. Names the dataspace and states the authority's DID. |
TsgDeployment | The complete desired deployment described by this file. |
spec.stacks[] | Deployable TSG runtimes. A stack owns Kubernetes resources, and is the unit of apply, inventory and prune. |
stack.managedTenancy | Managed service configuration. Organizations are created or imported through Tenant Management. |
Several organizations may deploy services for the same dataspace from separate files. Every file states the membership authority explicitly. Organizations are managed runtime records with canonical IDs, service bindings and lifecycle state; the deployment file does not create them from names or tenant slugs.
Document
| Field | Type | Required | Description |
|---|---|---|---|
apiVersion | String | Yes | tsg.dataspac.es/v1alpha2 |
kind | String | Yes | TsgDeployment |
metadata.name | String (DNS label) | Yes | Deployment identity. Derives inventory names and ownership labels, so two deployments in one namespace must not share it. |
spec | DeploymentSpec | Yes |
spec
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
namespace | String (DNS label) | Yes | Kubernetes namespace for everything in this file. | |
dataspace | Dataspace | Yes | The trust domain being joined. | |
admin | Admin | Yes | Initial administrator of the SSO bridge. | |
auth | Auth | Client authentication method for the components. | ||
database | Database | PostgreSQL topology. | ||
routing | Routing | Ingress and Gateway API settings. | ||
images | Images | Image overrides per component type. | ||
patches | Array<Patch> | [] | Patches applied to the whole bundle. | |
extraManifests | Array<String> | [] | Extra manifest paths, relative to this file. | |
stacks | Array<Stack> | Yes | At least one deployable runtime. |
spec.dataspace
| Field | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Dataspace name, a lowercase DNS label. |
authority.did | String | Yes | did:web: identifier of the participant issuing the membership credential. |
authority.domain | String | Yes | Host serving the authority's credential type URLs. |
authority.credentialType | String | Yes | Credential type the components accept as proof of membership. |
spec.admin
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
username | String | Yes | Initial administrator username. | |
email | String | Defaults to <username>@example.com. | ||
require2FA | Boolean | false | Force TOTP enrolment on first login. | |
passwordSecret | SecretRef | Yes | Existing Secret holding the initial password. |
spec.auth
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
clientAuthMethod | String | client_secret_post | client_secret_post or private_key_jwt. | |
publicKeysDir | String | keys | Committed directory of public JWKs, relative to this file. |
With private_key_jwt, the public JWK is a render input. The renderer does not
contact the cluster and cannot read it from a Secret. Generate a pair with
tsg keys generate <stack> <client>, which writes
keys/<stack>-<client>.jwk to commit and the private half to a git-ignored
directory. The CLI refuses a JWK containing a d parameter.
spec.database
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | String | perStack | perStack or shared. | |
defaults.instances | Integer | 1 | CloudNativePG instances per cluster. | |
defaults.storageSize | String | 1Gi | Volume size per instance. |
Every component owns its database through a CloudNativePG DatabaseRole. The
login role is named <stack>__<component>, and a separate basic-auth Secret
holds its password. A component cannot read tables owned by another component,
whether they share a stack or a Cluster.
shared puts every stack on one PostgreSQL instance. It saves resources in a
demo, but it is not a hard security boundary because all stacks share the
instance and its capacity. perStack is the default. Use it between
organisations that do not trust each other.
PostgreSQL still grants CONNECT on a database to PUBLIC. Any role in the
instance can connect and read catalog metadata such as table names, but table
contents remain available only to their owner.
spec.routing
At least one of ingress.enabled and gateway.enabled must be true. You can
enable both while moving hosts from Ingress to Gateway API.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
ingress.enabled | Boolean | true | ||
ingress.className | String | nginx | ||
ingress.clusterIssuer | String | letsencrypt | cert-manager issuer annotation. | |
ingress.annotations | Object | Added to every rendered Ingress. | ||
gateway.enabled | Boolean | false | ||
gateway.className | String | eg | GatewayClass name. | |
gateway.clusterIssuer | String | cert-manager ClusterIssuer annotation. | ||
gateway.issuer | String | cert-manager namespaced Issuer annotation. | ||
gateway.redirectHTTP | Boolean | true | Emit an HTTP listener and an HTTP→HTTPS route. | |
gateway.allowedRoutesFrom | String | Same | Same or All. | |
gateway.annotations | Object | Added to every rendered Gateway. |
The CLI renders one Gateway for each distinct host and assigns it to the first
component on that host. Other components add an HTTPRoute. See the
deployment guide for the
mergeGateways requirement.
spec.images
Image settings are keyed by component type. Set either tag or digest, but
not both. If you omit both, the CLI derives the tag from its version.
| Field | Type | Description |
|---|---|---|
controlPlane | Image | |
ssoBridge | Image | |
wallet | Image | |
tenantManagement | Image | Manager image override. |
dataPlanes.<type> | Image | Keyed by data plane type, e.g. http-data-plane. |
useCaseApps.<type> | Image | Keyed by app type, e.g. analytics-orchestration. |
Image
| Field | Type | Description |
|---|---|---|
repository | String | Defaults to the component's own TSG image. |
tag | String | Defaults to v<cli-version>. |
digest | String | sha256:<64 hex>. Pin this for committed bundles. |
A digest is immutable, so the rendered imagePullPolicy becomes
IfNotPresent; a tag renders Always.
spec.stacks
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | String (DNS label) | Yes | Names every rendered resource and the apply set. | |
name | String | Yes | Display name. | |
host | String (DNS name) | Yes | Public host. No scheme, no path. | |
routing | String | path | path or subdomain. | |
database | { instances, storageSize } | Per-stack sizing. Rejected in shared mode. | ||
components | Components | |||
managedTenancy | { adminUserId, selfServiceCreation } | Yes | Stable platform administrator UUID and optional self-service creation flag. | |
patches | Array<Patch> | [] | Applied after the global ones, to this stack's set only. | |
extraManifests | Array<String> | [] | Paths relative to the configuration file. |
With path routing, every component shares one host. The SSO bridge uses the
root, the wallet uses /wallet, the control plane uses /control-plane, and a
keyed component uses /<key>. This needs one certificate and one DNS record.
With subdomain routing, each component gets its own host.
stack.components
| Field | Type | Description |
|---|---|---|
ssoBridge | Workload | Always rendered; this only overrides it. |
wallet | Workload | Always rendered; this only overrides it. |
controlPlane | Workload + enabled | Must explicitly set enabled: true. |
tenantManagement | Workload | Must explicitly declare {} or workload settings. |
dataPlanes | Record<key, DataPlane> | Keyed by component name. |
useCaseApps | Record<key, UseCaseApp> | Keyed by component name. |
Wallet and SSO Bridge remain established stack components. Managed tenancy also requires an explicit Control Plane and Tenant Management component. The renderer rejects missing prerequisites rather than adding services to an old topology.
Tenant Management creates or imports organizations and provisions service
bindings. Each service has persistent session/encryption Secrets. Browser clients
are shared public clients; the manager's provisioning client has platform kind.
All API tokens use strict audience validation. Removed auth selectors
tokenValidationMode and legacyAccessTokenAudiences are rejected.
DataPlane
Everything in Workload, plus:
| Field | Type | Default | Description |
|---|---|---|---|
type | String | the key | http-data-plane or analytics-data-plane. |
subPath | String | the key | Path segment, in path routing. |
dnsPrefix | String | the key | DNS label, in subdomain routing. |
database | Boolean | true | Set false for a data plane that keeps no state. |
The HTTP data plane executes dataflows. Before deployment, provision its owner
through Tenant Management, register the worker in the managed Control Plane and
import its scoped OAuth credentials into sso-<stack>-<worker>-secret.
config.controlPlane must contain the real controlplaneId, dataplaneId and
nonempty executionScopes[] with issuer, matching controlPlaneId and canonical
tenantId UUID. Set config.auth.openIdConfigurationURL to its managed issuer.
The CLI never generates credentials or canonical tenant IDs for these owners.
Analytics workers also need an explicitly provisioned dedicated job namespace,
unprivileged job service account and approved image list under
config.orchestration. Runtime RBAC is limited to that job namespace, pod reads
in the configured worker namespace and get on the exact namespace at cluster
scope. See the worker prerequisites.
participant is retained in frozen input schemas but rejected when rendering.
Migrate to canonical execution scopes. Dataset metadata and private backend
configuration belong to the Control Plane and are published through its API.
UseCaseApp
Everything in Workload, plus type, subPath and dnsPrefix.
Participant seed bindings are rejected.
| Type | Behavior |
|---|---|
dataspace-starter | Static workload, public shared browser client and service proxy targets. |
gaiax-credential-workbench | Requires separate managed server configuration and browser registration; rejected by this renderer. |
analytics-orchestration | Requires separate tenant-specific provisioning; rejected by this renderer. |
Starter accepts config.clientId, defaulting to dataspace-starter, and
config.tenants, defaulting to an empty list. Each tenant choice requires
slug, name, canonical tenantId UUID and audience: control-plane-api.
These are references to organizations that already exist. The CLI provisions no
organization or grant from this list. Removed authorizationMode is rejected.
Component keys share one namespace because they name rendered resources.
wallet, control-plane, sso-bridge and tenant-management are reserved.
Workload
The typed customisation block covers common workload settings. Use a patch for other Kubernetes fields.
| Field | Type | Description |
|---|---|---|
replicas | Integer | |
resources | Object | requests and limits, each { cpu, memory }. |
nodeSelector | Object | |
routing.className | String | Overrides the Ingress or Gateway class for this component. |
routing.host | String | Explicit public DNS host in v1alpha2; no scheme, path or wildcard. |
routing.annotations | Object | Added to this component's Ingress, Gateway and HTTPRoute. |
config | Object | Application configuration, merged over the rendered defaults. |
config is passed to the component's own configuration document. See the
per-application configuration references under Applications.
SecretRef
| Field | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Name of an existing Secret in the namespace. |
key | String | Yes | Key within it. |
Patches
The CLI applies patches and extra manifests after rendering and before apply. This composition step covers Kubernetes settings outside the typed schema.
| Field | Type | Default | Description |
|---|---|---|---|
target | Object | group, version, kind, name, namespace, labelSelector, annotationSelector. | |
merge | Object | Maps merge recursively, arrays are replaced. | |
jsonPatch | Array | RFC 6902 operations. Required for list surgery. | |
delete | Boolean | false | Remove the matched resources from the bundle. |
optional | Boolean | false | Allow a target that matches nothing. |
allowMultiple | Boolean | false | Allow a target that matches several resources. |
patches:
- target:
kind: Deployment
labelSelector: tsg.dataspac.es/component=control-plane
merge:
spec:
template:
spec:
imagePullSecrets:
- name: registry-credentials
- Targets are Kustomize-compatible.
- Patches run in declared order. Global patches run before stack patches.
- Extra manifests load before patches, so patches can also target added objects.
- A target matching zero resources fails unless
optional: true; one matching several fails unlessallowMultiple: true. - A deleted resource is absent from the bundle, and therefore prune-eligible.
The CLI adds identity labels before patches, which makes them available to a
labelSelector. It validates the labels after patching and rejects changes to
them.
Rendered names are part of the contract because patches target them. Component
resources are <stack>-tsg-<component>, with suffixes such as -config,
-ingress, -gateway, -route, -pvc, -role or -binding. Names are
namespace-independent.
Secrets
Rendered resources contain no credential values. They only reference existing
Secrets, for database credentials, OAuth client credentials, private_key_jwt
keys, the initial administrator password and persistent session/encryption keys.
Before applying anything, tsg deploy checks that every referenced Secret and
key exists. A missing credential then fails at the CLI instead of leaving a
workload in CrashLoopBackOff.
Deliver Secrets with SOPS, Sealed Secrets, External Secrets, or kubectl. For
a demo:
tsg secrets generate participant.yaml --apply
It never overwrites material that already exists.
Patches, extra manifests and application config are caller-controlled and can
still introduce credentials, so inspect those inputs before committing a bundle.
Versioning
The CLI versions three formats independently:
| Format | Lives in | Marker |
|---|---|---|
| Input config | your Git repository | apiVersion |
bundle.json | rendered output | formatVersion |
| Inventory | the cluster | formatVersion |
This separation lets the input schema change without invalidating every bundle, and lets the bundle format change without invalidating cluster inventories. A CLI may read inventory written months earlier. It rejects an inventory format newer than it understands.
A released input version is not edited. Changes that break the input contract use a new version. Migrate a file with:
tsg migrate participant.yaml -w
The frozen v1alpha1 schema remains accepted and upgradeable. Its old seed
semantics cannot render against managed-only runtime services. After migrate,
remove participant seeds, declare managed tenancy and explicit manager/control
plane components, then create or import organizations through Tenant Management.
The converter does not assign canonical IDs or import tenant state.
The command rewrites the file at the newest version this CLI serves. It does not preserve comments or key order, so review the diff.
An apiVersion newer than any your CLI serves means the file was written for a
newer CLI; upgrade rather than editing the marker.
Production notes
private_key_jwtremoves shared client secrets from the deployment. Setauth.clientAuthMethod: private_key_jwt, generate a pair per client withtsg keys generate, and commit only the public halves.require2FAon the admin forces TOTP enrolment on first login.database.mode: perStackgives each stack its own PostgreSQL instance.- Pin digests for anything committed, so the bundle and the running image cannot drift apart.
- Review the rendered output.
tsg renderdoes not contact the cluster, so the manifests in a merge request are exactly what the controller will apply.