Skip to main content

Link Settings

A link expresses "I have a runtime relationship with this component". Every link is {target, settings} — a component reference plus an optional flat map of values. The agent reads the settings to derive security-group rules, load-balancer registrations, IAM grants, injected environment, and more.

This page is the settings contract for every link type the platform handles. For the concepts — how links differ from dependencies and parameters, how the two-phase resolver works, and what a custom agent must implement — see Component Links.

One shape, always

The link format is always generic: target plus settings. There are no specialised or typed link schemas on the wire — port, protocol, consumer group, access level and mount path are all keys in the settings map. SDKs wrap this in typed linkToXxx() helpers or typed settings shapes applied with satisfies, but the wire contract never changes.


Security group membership

A compute component (VM, ECS task, EKS node, …) links to a SecurityGroup to declare membership.

No settings. The presence of the link is the only signal, and no dependency on the security group is required or correct.


Managed security-group traffic rules

When compute component A links to compute component B, the agent derives egress rules on A's managed security group and ingress rules on B's.

SettingTypeRequiredDescription
fromPortnumberyesStart of the port range. A link without it is skipped.
toPortnumbernoEnd of the port range. Defaults to fromPort.
protocolstringnotcp (default), udp or icmp.

The managed group is named fractal-mgd-{componentId}, tagged fractal-managed=true, and its id is published as the managedGroupId output field. Do not attempt to manage rules on it directly — the managed group is created and owned by the agent — never manage its rules directly.


Load balancer

Load balancers use two link directions with different settings.

SettingTypeRequiredDescription
instanceIdstringyes (EC2 targets)EC2 instance id.
ipstringyes (ECS / EKS / IP targets)IP address of the target.
vpcIdstringyesVPC the target lives in.
tgPortnumbernoPort on the target. Default 80.
availabilityZonestringnoAZ, for zone-aware routing.
loadBalancerArnstringyes (ALB target)ARN of the upstream ALB.
hcProtocolstringnoHealth check protocol. Default HTTP.
hcPathstringnoHealth check path. Default /.
hcIntervalSecondsnumbernoHealth check interval. Default 30.

A component that logically drives the load balancer (a DNS or gateway component) links to it and declares the frontend listener.

SettingTypeRequiredDescription
listenerPortnumberyesFrontend port, e.g. 443.
listenerProtocolstringyesHTTP, HTTPS, TCP, …
targetComponentIdstringyesId of the linked backend component to forward to.

Consumer → RelationalDatabase

Declares "I use this database". The agent grants the consumer its own database role scoped by access, and injects vendor-neutral connection environment.

SettingTypeRequiredDescription
access'read-write' | 'read-only'yesScope of the granted database role.

access is the canonical key and has no default — a link without it is rejected rather than guessed at, because read-only silently breaks a writer and read-write silently over-grants a reader. Two older spellings are still accepted, because the authoring surface, not the author, decided which one a link carries:

Alias keyAccepted valuesWhere it comes from
accessModeread, write, readWriteThe catalogue's shared Storage.*.RelationalDatabase link declaration — so UI-authored links.
roleNamereader, data_reader, writer, data_writer, adminBlueprints written against the Azure Flexible Server path.

access wins when more than one is present.

Injected into the consumer: DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD_REF. Connection facts are published by the database as output fields, not carried on the link. The raw password never enters output fields — DB_PASSWORD_REF carries only a secret-store reference the runtime resolves at launch.


Compute → ObjectStorage

Declares "I use this bucket". The agent ensures the consumer has an identity (creating and attaching one when absent — GCP service account, AWS instance profile, Azure managed identity), grants that identity a bucket role scoped by access, and publishes the bucket URI to the consumer.

SettingTypeRequiredDescription
access'read' | 'write' | 'read-write'yesScope of the granted storage role.

Role mapping is vendor-agnostic and never a broad scope:

accessAWSAzureGCP
reads3:GetObject + ListBucketStorage Blob Data ReaderobjectViewer
writes3:PutObjectStorage Blob Data ContributorobjectCreator
read-writebothStorage Blob Data ContributorobjectAdmin

Workload / DataProcessingJob → MessagingEntity

Declares "I publish to / subscribe from this topic or queue". The messaging agent grants the source the right IAM/ACL on the entity and wires consumer-side parameters.

SettingTypeRequiredDescription
access'publish' | 'subscribe' | 'publish-subscribe'yesDirection of the relationship.
consumerGroupstringnoConsumer group / subscription identifier. Only meaningful when access includes subscribe.
startingPositionstringnostart, end, or an ISO timestamp.

The schema is identical for both source types.


DataProcessingJob → Datalake

Declares "I read from / write to this lake for the given purpose". The agent injects the corresponding URI into the job runtime (LAKE_RAW_URI, LAKE_CURATED_URI, LAKE_CHECKPOINT_URI). Multiple links per job are allowed and may target different lakes.

SettingTypeRequiredDescription
purpose'raw' | 'curated' | 'checkpoint'yesIngestion zone, processed outputs, or streaming state.
pathstringnoSub-path within the lake. The agent composes <lake-uri>/<path>.

ComputeCluster → EventHub instance

Azure only. Wires EventHub connection strings into the Spark configuration via Databricks secret scopes.

SettingTypeRequiredDescription
keyVaultSecretNamestringyesName of the management Key Vault secret holding the EventHub connection string.
consumerGroupstringnoEventHub consumer group. Default $Default.
startingPositionstringnostart, end (default), or an ISO timestamp.

Consumer → IdentityProvider

Declares "I am an OAuth client of this identity provider". Each such link provisions exactly one app client on the pool/tenant. Guardrails on the identity provider Component (MFA, password policy, allowed OAuth flows) cap what any link's client may request.

SettingTypeRequiredDescription
clientType'web' | 'spa' | 'machine'yesweb = confidential (login flow + secret); spa = public; machine = client-credentials, no redirect.
redirectUrisstring[]yes for web / spaOAuth callback URLs.
logoutUrisstring[]noPost-logout redirect URLs. Browser flows only.
scopesstring[]noRequested OAuth scopes.

Injected into the consumer: OIDC_ISSUER_URI, OIDC_CLIENT_ID, OIDC_JWKS_URI, OIDC_SCOPES, and — for confidential clients — OIDC_CLIENT_SECRET_REF. The raw client secret is never placed in output fields; it lives in the vendor secret store and the reference is resolved at launch.


Consumer → Unmanaged

Declares "I consume this external service". The agent grants the consumer read access to the component's referenced secret and injects the config plus a secret reference — never the raw secret.

SettingTypeRequiredDescription
envPrefixstringnoPrefix for the injected config and secret reference. Defaults to the target component's id.

Environment-secret references

Any component parameter or link setting value may reference an environment secret by short name instead of carrying the raw value.

// Define once on the environment:
env.withSecret({shortName: 'svc-token', displayName: 'Service token', value: TOKEN});

// Reference from a parameter …
UnmanagedAi({secret: secretRef('openai-api-key')});
// … or from a link setting:
link(vm, svc, {token: secretRef('svc-token')});

The wire shape is the tagged value {"$envSecret": "<shortName>"}. The agent resolves the reference from the environment secret store at reconciliation time — parameters through the short-name resolver, link settings through the link framework before handlers read them — and never writes the raw value into output fields. Secrets always surface downstream as a *_REF reference.

Use this for every secret an external component or a link needs. The raw value stays in the secret store.


Link-derived work is segregated so that creating a machine is never conflated with deploying software onto it:

  1. Component creation — a VM is created with its identity (created and associated at create time when the VM has a configured identity or any injectable link).
  2. Link resolution — a generic hook invoked after the component is Active, every patrol cycle, idempotent. For a VM it assigns roles and permissions to the VM identity: a scoped object-storage role from the link's access, read on a linked Unmanaged component's referenced secret. Grants are diffed against prior state held in the VM's linkGrants output field, so removing a link revokes the access. No environment is delivered to the box in this phase.
  3. Workload deployment — the workload on the VM consumes the resolved values (bucket URI, *_SECRET_REF) as its runtime environment.

Anti-patterns

  • Using a dependency when you mean a link. If A only needs B to exist, that is a dependency. If A needs to read B's URL or secret, that is a link.
  • Adding a dependency alongside a membership link. Security-group and network-policy membership needs the link only.
  • Putting connection facts on the link. Hosts, ports, URIs, client ids and secrets are published by the target as output fields and injected downstream. A link carries intent, not data.
  • Putting a raw secret in a parameter or setting. Use secretRef and an environment secret.