Skip to main content
Version: v0.20.0

Configuration reference

A TSG deployment is described by one YAML document. This page is the reference for apiVersion: tsg.dataspac.es/v1alpha1; run tsg --version to see which versions your CLI accepts.

apiVersion: tsg.dataspac.es/v1alpha1
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.

ConceptWhat it is
spec.dataspaceThe trust domain the stacks in this file join. Names the dataspace and states the authority's DID.
TsgDeploymentThe 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.participantSeeds[]Dataspace identities onboarded into that runtime. They own no Kubernetes resources.

Several organisations may deploy one dataspace from separate files. Each file therefore states the authority instead of deriving it from the stacks it contains. If a file includes the issuer, dataspace.authority.did must match the DID served by that issuer. The CLI rejects a mismatch.

Every participant is a tenant, even when a stack has only one. Its DID is always did:web:<host>:tenants:<id>. This keeps the DID stable if another participant is added later. Credentials, contract agreements, and catalog entries can continue to refer to the original DID.

Document

FieldTypeRequiredDescription
apiVersionStringYestsg.dataspac.es/v1alpha1
kindStringYesTsgDeployment
metadata.nameString (DNS label)YesDeployment identity. Derives inventory names and ownership labels, so two deployments in one namespace must not share it.
specDeploymentSpecYes

spec

FieldTypeRequiredDefaultDescription
namespaceString (DNS label)YesKubernetes namespace for everything in this file.
dataspaceDataspaceYesThe trust domain being joined.
adminAdminYesInitial administrator of the SSO bridge.
authAuthClient authentication method for the components.
databaseDatabasePostgreSQL topology.
routingRoutingIngress and Gateway API settings.
imagesImagesImage overrides per component type.
patchesArray<Patch>[]Patches applied to the whole bundle.
extraManifestsArray<String>[]Extra manifest paths, relative to this file.
stacksArray<Stack>YesAt least one deployable runtime.

spec.dataspace

FieldTypeRequiredDescription
nameStringYesDataspace name, a lowercase DNS label.
authority.didStringYesdid:web: identifier of the participant issuing the membership credential.
authority.domainStringYesHost serving the authority's credential type URLs.
authority.credentialTypeStringYesCredential type the components accept as proof of membership.

spec.admin

FieldTypeRequiredDefaultDescription
usernameStringYesInitial administrator username.
emailStringDefaults to <username>@example.com.
require2FABooleanfalseForce TOTP enrolment on first login.
passwordSecretSecretRefYesExisting Secret holding the initial password.

spec.auth

FieldTypeRequiredDefaultDescription
clientAuthMethodStringclient_secret_postclient_secret_post or private_key_jwt.
publicKeysDirStringkeysCommitted 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

FieldTypeRequiredDefaultDescription
modeStringperStackperStack or shared.
defaults.instancesInteger1CloudNativePG instances per cluster.
defaults.storageSizeString1GiVolume 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.

FieldTypeRequiredDefaultDescription
ingress.enabledBooleantrue
ingress.classNameStringnginx
ingress.clusterIssuerStringletsencryptcert-manager issuer annotation.
ingress.annotationsObjectAdded to every rendered Ingress.
gateway.enabledBooleanfalse
gateway.classNameStringegGatewayClass name.
gateway.clusterIssuerStringcert-manager ClusterIssuer annotation.
gateway.issuerStringcert-manager namespaced Issuer annotation.
gateway.redirectHTTPBooleantrueEmit an HTTP listener and an HTTP→HTTPS route.
gateway.allowedRoutesFromStringSameSame or All.
gateway.annotationsObjectAdded 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.

FieldTypeDescription
controlPlaneImage
ssoBridgeImage
walletImage
dataPlanes.<type>ImageKeyed by data plane type, e.g. http-data-plane.
useCaseApps.<type>ImageKeyed by app type, e.g. analytics-orchestration.

Image

FieldTypeDescription
repositoryStringDefaults to the component's own TSG image.
tagStringDefaults to v<cli-version>.
digestStringsha256:<64 hex>. Pin this for committed bundles.

A digest is immutable, so the rendered imagePullPolicy becomes IfNotPresent; a tag renders Always.

spec.stacks

FieldTypeRequiredDefaultDescription
idString (DNS label)YesNames every rendered resource and the apply set.
nameStringYesDisplay name.
hostString (DNS name)YesPublic host. No scheme, no path.
routingStringpathpath or subdomain.
database{ instances, storageSize }Per-stack sizing. Rejected in shared mode.
componentsComponents
participantSeedsArray<ParticipantSeed>[]Identities onboarded into this runtime.
patchesArray<Patch>[]Applied after the global ones, to this stack's set only.
extraManifestsArray<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

FieldTypeDescription
ssoBridgeWorkloadAlways rendered; this only overrides it.
walletWorkloadAlways rendered; this only overrides it.
controlPlaneWorkload + enabledOpt in with enabled: true.
dataPlanesRecord<key, DataPlane>Keyed by component name.
useCaseAppsRecord<key, UseCaseApp>Keyed by component name.

A wallet and an SSO bridge are always rendered. A control plane is what makes a participant able to trade data rather than only hold credentials.

The control plane serves all participant seeds in its stack and requires at least one. The CLI renders per-tenant IAM, wallet bindings, memberships and catalogs under initTenants. Data planes and server use-case apps select a seed with participant: <seed-id>. With exactly one seed, selection is automatic; an ambiguous or unknown selection is rejected. A wallet-only stack may start empty and onboard participants later.

Connector discovery and data-plane protocol URLs include /tenants/<slug>. Management clients send the tenant slug explicitly. These bindings select a tenant; they do not restrict OAuth clients to that tenant.

DataPlane

Everything in Workload, plus:

FieldTypeDefaultDescription
typeStringthe keyhttp-data-plane or analytics-data-plane.
subPathStringthe keyPath segment, in path routing.
dnsPrefixStringthe keyDNS label, in subdomain routing.
databaseBooleantrueSet false for a data plane that keeps no state.
participantStringsole seedParticipant seed id. Required when the stack has multiple seeds.

The HTTP data plane executes dataflows. Dataset metadata and private asset configuration live in the control plane. Add dataspace-starter under useCaseApps to publish and manage them through the SDK. The data plane still stores operational dataflow state and request logs, so keep its database unless another persistence mechanism replaces it.

UseCaseApp

An application built on top of a stack. Everything in Workload, plus type, subPath, dnsPrefix and participant as above. Only server apps accept participant; static apps have no fixed identity. Analytics Orchestration selects an analytics data plane for the same participant.

TypeShapeWhat it gets
analytics-orchestrationserverA database, a confidential OAuth client and a rendered config.
dataspace-starterstaticA workload, a route and a public PKCE client without credentials.
gaiax-credential-workbenchstaticA workload and a route.

The image profile determines the shape. A server app is configured like a component and reaches its stack through in-cluster Services. A static app is a built single-page application that calls the APIs from the browser. Dataspace Starter's profile also registers a public OAuth client for browser login. It has no client secret.

useCaseApps:
analytics-orchestration: {}
credentials:
type: gaiax-credential-workbench
subPath: /credentials

Component keys share one namespace because they name the rendered resources. wallet, control-plane and sso-bridge are reserved.

stack.participantSeeds

FieldTypeRequiredDefaultDescription
idString (DNS label)YesTenant slug, and the last segment of the DID.
nameStringYesDisplay name.
issuerBooleanfalseThis participant issues the membership credential.
credentialSubjectObjectExtra claims the authority puts into this participant's credential.
preAuthorizedCodeSecretSecretRefExisting Secret holding the OID4VCI pre-authorized code.
membersArray<{ identifier, role }>[]role is owner, admin (default) or member.

At most one participant across the file may be the issuer. Removing a seed is not an offboarding operation and removes no Kubernetes resources.

Workload

The typed customisation block covers common workload settings. Use a patch for other Kubernetes fields.

FieldTypeDescription
replicasInteger
resourcesObjectrequests and limits, each { cpu, memory }.
nodeSelectorObject
routing.classNameStringOverrides the Ingress or Gateway class for this component.
routing.annotationsObjectAdded to this component's Ingress, Gateway and HTTPRoute.
configObjectApplication 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

FieldTypeRequiredDescription
nameStringYesName of an existing Secret in the namespace.
keyStringYesKey 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.

FieldTypeDefaultDescription
targetObjectgroup, version, kind, name, namespace, labelSelector, annotationSelector.
mergeObjectMaps merge recursively, arrays are replaced.
jsonPatchArrayRFC 6902 operations. Required for list surgery.
deleteBooleanfalseRemove the matched resources from the bundle.
optionalBooleanfalseAllow a target that matches nothing.
allowMultipleBooleanfalseAllow 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 unless allowMultiple: 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 OID4VCI pre-authorized codes.

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:

FormatLives inMarker
Input configyour Git repositoryapiVersion
bundle.jsonrendered outputformatVersion
Inventorythe clusterformatVersion

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 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_jwt removes shared client secrets from the deployment. Set auth.clientAuthMethod: private_key_jwt, generate a pair per client with tsg keys generate, and commit only the public halves.
  • require2FA on the admin forces TOTP enrolment on first login.
  • database.mode: perStack gives 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 render does not contact the cluster, so the manifests in a merge request are exactly what the controller will apply.