@fractal_cloud/sdk 1.5.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  The Fractal Cloud TypeScript SDK lets platform engineers and application developers define cloud-agnostic infrastructure blueprints in TypeScript and deploy them to any supported cloud provider.
13
13
 
14
- Infrastructure is modelled as reusable architectural building blocks — **Fractals** — that can be validated, governed, and evolved over time. Live Systems map those blueprints to concrete cloud resources without ever touching vendor-specific tooling or DSLs.
14
+ Infrastructure is modelled as reusable architectural building blocks — **Fractals** — that can be validated, governed, and evolved over time. A **Live System** is produced by selecting, per component, a concrete **Offer** from the catalogue — without ever touching vendor-specific tooling or DSLs.
15
15
 
16
16
  ## Why Fractal
17
17
 
@@ -28,35 +28,35 @@ Fractal Cloud takes a different approach: define infrastructure as **architectur
28
28
 
29
29
  ### Fractal (Blueprint)
30
30
 
31
- A Fractal is a governed, reusable infrastructure pattern. It defines what an infrastructure system is allowed to becomponents, their relationships, and architectural intent without referencing any cloud provider. Fractals are registered with the Fractal Cloud API and can be composed into higher-order Fractals.
31
+ A Fractal is a governed, reusable infrastructure pattern. It references **abstract Components only** never offers or vendorsand declares their structure (dependencies and links) and their **guardrails** (locked parameters). Adding a vendor never requires editing an existing Fractal.
32
32
 
33
33
  ### Live System
34
34
 
35
- A Live System is a running instance of a Fractal. It maps each abstract blueprint component to a concrete provider-specific **Offer** and supplies the vendor parameters the Fractal Automation Engine needs to provision and reconcile cloud resources. There are no state files the cloud is the source of truth.
35
+ A Live System is a running instance of a Fractal. It is built by **per-component offer selection**: each abstract Component is mapped to one concrete provider-specific **Offer** that carries the vendor parameters the Automation Engine needs. There is no global provider — mixed-vendor live systems are normal. There are no state files; the cloud is the source of truth.
36
36
 
37
- ## What this SDK supports
37
+ ### The Catalogue three levels
38
38
 
39
- - Definition of Fractal Blueprints using cloud-agnostic component helpers
40
- - Deployment of Blueprints to Fractal Cloud
41
- - Definition of Live Systems with typed, provider-specific helpers
42
- - Deployment of Live Systems to any Fractal Cloud Environment
43
- - Personal and organisation-owned accounts
39
+ ```
40
+ Level 1 COMPONENT {domain}.{name} e.g. Storage.ObjectStorage
41
+ Abstract capability contract. Referenced by blueprints. Never provisioned.
42
+
43
+ Level 2 SERVICE {domain}.{deliveryModel}.{name} delivery-model contract. Never provisioned.
44
+
45
+ Level 3 — OFFER {domain}.{deliveryModel}.{name} + a provider
46
+ Concrete, vendor-specific. The only level that maps to real infra.
47
+ ```
48
+
49
+ A blueprint references a Component (`Storage.ObjectStorage`); a Live System selects any Offer that `satisfies` it (`AwsS3`, `AzureBlob`, `GcsBucket`, `MinIO`, …). Selection is **compile-checked**: an offer that does not satisfy a slot's Component is a type error (and is also rejected at runtime).
44
50
 
45
- ### Infrastructure domains
51
+ `provider` is a **vendor** axis (`AWS | Azure | GCP | OCI | Hetzner | Aruba | RedHat | VMware`). `IaaS | PaaS | CaaS | SaaS | FaaS` is the separate `deliveryModel` axis. Vendor-neutral self-hosted offers (e.g. `Kafka`, `Prometheus`, `MinIO` on any cluster) omit `provider`.
46
52
 
47
- | Domain | Blueprint types | Live system offers | Providers |
48
- |--------|----------------|-------------------|-----------|
49
- | **NetworkAndCompute** | VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform | 41 offers | AWS, Azure, GCP, OCI, Hetzner, VMware, OpenShift, Aruba |
50
- | **CustomWorkloads** | Workload | Satisfied by PaaS/CaaS offers (ECS, Container Apps, Cloud Run, OpenShift, Kubernetes, etc.) | AWS, Azure, GCP, OCI, OpenShift, CaaS |
51
- | **Storage** | FilesAndBlobs, RelationalDbms, RelationalDatabase, DocumentDbms, DocumentDatabase, ColumnOrientedDbms, ColumnOrientedEntity, KeyValueDbms, KeyValueEntity, GraphDbms, GraphDatabase, Search, SearchEntity, Unmanaged | 27 offers | AWS, Azure, GCP, Aruba, CaaS, SaaS |
52
- | **Messaging** | Broker, Entity (PaaS + CaaS), Unmanaged | 12 offers | Azure, GCP, CaaS |
53
- | **BigData** | DistributedDataProcessing, ComputeCluster, DataProcessingJob, MlExperiment, Datalake, Unmanaged | 21 offers | AWS, Azure, GCP, Aruba, CaaS |
54
- | **APIManagement** | PaaS ApiGateway, CaaS ApiGateway, Unmanaged | 6 offers | AWS, Azure, GCP, CaaS |
55
- | **Observability** | Monitoring, Tracing, Logging, Unmanaged | 4 offers | CaaS |
56
- | **Security** | ServiceMesh, Unmanaged | 2 offers | CaaS |
57
- | **Custom** | Any (via `Custom.blueprint()`) | Any (via `Custom.offer()`) | Any |
53
+ ### Guardrails vs operations
58
54
 
59
- > Custom aria agents can register additional domains, services, and offers beyond this built-in set. See [Custom Aria Components](#custom-aria-components) below.
55
+ | Surface | Who sets it | When | Effect |
56
+ |---|---|---|---|
57
+ | **Guardrail** — `.withXxx()` on a Component, at design time | The architect, inside `createFractal` | Authoring | Sets a neutral parameter and **locks** it. Devs cannot override; a later write throws. |
58
+ | **Operation** — a verb on the Fractal `operations` interface | The consuming dev, via `.specialize()` | Specialization | Application-level intent (`withCollation`, `withDatabases`, `withRoutes`). Writes open params or adds child components. Never a pass-through infra knob. |
59
+ | **Vendor config** — an Offer's config object | The dev, at offer selection | Live System | Vendor-only knobs (`bucketRegion`, `amiId`, `resourceGroup`, …). |
60
60
 
61
61
  ## Installation
62
62
 
@@ -70,464 +70,290 @@ Requires Node.js 18+ and TypeScript 5+.
70
70
 
71
71
  ## Quick start
72
72
 
73
- The following example defines a cloud-agnostic blueprint with a VPC, a subnet, a security group, and two VMs then deploys it on AWS.
73
+ The following defines a cloud-agnostic blueprint (a VPC, a subnet, a security group, two VMs, and a container platform), then specializes it and selects AWS offers to build and deploy a Live System.
74
74
 
75
- ### 1. Define the blueprint (`fractal.ts`)
75
+ ### 1. Author the Fractal (`fractal.ts`)
76
76
 
77
77
  ```typescript
78
78
  import {
79
- BoundedContext, Fractal, KebabCaseString, OwnerId, OwnerType, Version,
80
- VirtualNetwork, Subnet, SecurityGroup, VirtualMachine,
79
+ createFractal,
80
+ VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform,
81
81
  } from '@fractal_cloud/sdk';
82
82
 
83
- const bcId = BoundedContext.Id.getBuilder()
84
- .withOwnerType(OwnerType.Personal)
85
- .withOwnerId(OwnerId.getBuilder().withValue(process.env['OWNER_ID']!).build())
86
- .withName(KebabCaseString.getBuilder().withValue('my-team').build())
87
- .build();
83
+ const boundedContextId = {
84
+ ownerType: 'Personal',
85
+ ownerId: process.env['OWNER_ID']!,
86
+ name: 'my-team',
87
+ };
88
88
 
89
- const apiServer = VirtualMachine.create({
90
- id: 'api-server',
89
+ export const fractal = createFractal({
90
+ id: 'standard-network',
91
91
  version: {major: 1, minor: 0, patch: 0},
92
- displayName: 'API Server',
92
+ boundedContextId,
93
+ blueprint: bp => {
94
+ const network = bp.add(
95
+ VirtualNetwork({id: 'main-network'})
96
+ .withCidrBlock('10.0.0.0/16')
97
+ .withRegion('us-east-1'),
98
+ );
99
+ const subnet = bp.add(
100
+ Subnet({id: 'app-subnet'}).withCidrBlock('10.0.1.0/24').dependsOn(network),
101
+ );
102
+ const sg = bp.add(
103
+ SecurityGroup({id: 'app-sg'})
104
+ .withIngressRules([{protocol: 'tcp', fromPort: 443, sourceCidr: '0.0.0.0/0'}])
105
+ .dependsOn(network),
106
+ );
107
+ const web = bp.add(VirtualMachine({id: 'web-server'}).withOsType('linux').dependsOn(subnet));
108
+ const api = bp.add(VirtualMachine({id: 'api-server'}).withOsType('linux').dependsOn(subnet));
109
+ const cluster = bp.add(
110
+ ContainerPlatform({id: 'app-cluster'}).withKubernetesVersion('1.29').dependsOn(subnet),
111
+ );
112
+
113
+ // Runtime link: web can reach api on 8080 (blueprint owns all links).
114
+ bp.link(web, api, {fromPort: 8080, protocol: 'tcp'});
115
+
116
+ return {network, subnet, sg, web, api, cluster};
117
+ },
93
118
  });
94
-
95
- const webServer = VirtualMachine.create({
96
- id: 'web-server',
97
- version: {major: 1, minor: 0, patch: 0},
98
- displayName: 'Web Server',
99
- }).withLinks([{target: apiServer, fromPort: 8080}]);
100
-
101
- const network = VirtualNetwork.create({
102
- id: 'main-network',
103
- version: {major: 1, minor: 0, patch: 0},
104
- displayName: 'Main VPC',
105
- cidrBlock: '10.0.0.0/16',
106
- })
107
- .withSubnets([
108
- Subnet.create({
109
- id: 'public-subnet',
110
- version: {major: 1, minor: 0, patch: 0},
111
- displayName: 'Public Subnet',
112
- cidrBlock: '10.0.1.0/24',
113
- }).withVirtualMachines([webServer, apiServer]),
114
- ])
115
- .withSecurityGroups([
116
- SecurityGroup.create({
117
- id: 'web-sg',
118
- version: {major: 1, minor: 0, patch: 0},
119
- displayName: 'Web Security Group',
120
- description: 'Allow SSH and HTTP',
121
- ingressRules: [
122
- {protocol: 'tcp', fromPort: 22, toPort: 22, sourceCidr: '0.0.0.0/0'},
123
- {protocol: 'tcp', fromPort: 80, toPort: 80, sourceCidr: '0.0.0.0/0'},
124
- ],
125
- }),
126
- ]);
127
-
128
- export const fractal = Fractal.getBuilder()
129
- .withId(
130
- Fractal.Id.getBuilder()
131
- .withBoundedContextId(bcId)
132
- .withName(KebabCaseString.getBuilder().withValue('my-iaas-fractal').build())
133
- .withVersion(Version.getBuilder().withMajor(1).withMinor(0).withPatch(0).build())
134
- .build(),
135
- )
136
- .withComponents([...network.components])
137
- .build();
138
119
  ```
139
120
 
140
- ### 2. Satisfy with AWS components (`aws_live_system.ts`)
121
+ ### 2. Select offers and build the Live System (`aws_live_system.ts`)
141
122
 
142
123
  ```typescript
143
124
  import {
144
- AwsVpc, AwsSubnet, AwsSecurityGroup, Ec2Instance,
145
- Environment, KebabCaseString, LiveSystem, OwnerId, OwnerType,
125
+ AwsVpc, AwsSubnet, AwsSecurityGroup, Ec2Instance, Eks,
146
126
  } from '@fractal_cloud/sdk';
147
- import {bcId, fractal} from './fractal';
148
-
149
- function bp(id: string) {
150
- const c = fractal.components.find(x => x.id.toString() === id);
151
- if (!c) throw new Error(`Blueprint component '${id}' not found`);
152
- return c;
153
- }
154
-
155
- export function getLiveSystem(): LiveSystem {
156
- const vpc = AwsVpc.satisfy(bp('main-network')).withEnableDnsSupport(true).build();
157
- const subnet = AwsSubnet.satisfy(bp('public-subnet')).withAvailabilityZone('eu-central-1a').build();
158
- const sg = AwsSecurityGroup.satisfy(bp('web-sg')).build();
159
- const web = Ec2Instance.satisfy(bp('web-server')).withAmiId('ami-0c55b159cbfafe1f0').withInstanceType('t3.micro').build();
160
- const api = Ec2Instance.satisfy(bp('api-server')).withAmiId('ami-0c55b159cbfafe1f0').withInstanceType('t3.small').build();
161
-
162
- return LiveSystem.getBuilder()
163
- .withId(LiveSystem.Id.getBuilder().withBoundedContextId(bcId)
164
- .withName(KebabCaseString.getBuilder().withValue('my-iaas-aws').build()).build())
165
- .withFractalId(fractal.id)
166
- .withGenericProvider('AWS')
167
- .withEnvironment(Environment.getBuilder()
168
- .withId(Environment.Id.getBuilder()
169
- .withOwnerType(OwnerType.Personal)
170
- .withOwnerId(OwnerId.getBuilder().withValue(process.env['OWNER_ID']!).build())
171
- .withName(KebabCaseString.getBuilder().withValue('dev').build()).build()).build())
172
- .withComponent(vpc).withComponent(subnet).withComponent(sg).withComponent(web).withComponent(api)
173
- .build();
174
- }
127
+ import {fractal} from './fractal';
128
+
129
+ const environment = {
130
+ ownerType: 'Personal',
131
+ ownerId: process.env['OWNER_ID']!,
132
+ name: 'dev',
133
+ };
134
+
135
+ export const liveSystem = fractal.specialize().toLiveSystem({
136
+ name: 'acme-net-aws',
137
+ environment,
138
+ select: {
139
+ 'main-network': AwsVpc({}),
140
+ 'app-subnet': AwsSubnet({}),
141
+ 'app-sg': AwsSecurityGroup({}),
142
+ 'web-server': Ec2Instance({amiId: 'ami-0c55b159cbfafe1f0', instanceType: 't3.micro'}),
143
+ 'api-server': Ec2Instance({amiId: 'ami-0c55b159cbfafe1f0', instanceType: 't3.small'}),
144
+ 'app-cluster': Eks({}),
145
+ },
146
+ });
175
147
  ```
176
148
 
149
+ Every component id must map to an offer whose `satisfies` matches its Component. Selecting, say, `AwsVpc({})` for `'app-subnet'` is a compile-time error.
150
+
177
151
  ### 3. Deploy (`index.ts`)
178
152
 
179
153
  ```typescript
180
- import {ServiceAccountCredentials, ServiceAccountId} from '@fractal_cloud/sdk';
181
- import {fractal} from './fractal';
182
- import {getLiveSystem} from './aws_live_system';
154
+ import {deploy} from '@fractal_cloud/sdk';
155
+ import {liveSystem} from './aws_live_system';
183
156
 
184
- const credentials = ServiceAccountCredentials.getBuilder()
185
- .withId(ServiceAccountId.getBuilder().withValue(process.env['SERVICE_ACCOUNT_ID']!).build())
186
- .withSecret(process.env['SERVICE_ACCOUNT_SECRET']!)
187
- .build();
157
+ const credentials = {
158
+ clientId: process.env['SERVICE_ACCOUNT_ID']!,
159
+ clientSecret: process.env['SERVICE_ACCOUNT_SECRET']!,
160
+ };
188
161
 
189
- await fractal.deploy(credentials);
190
- await getLiveSystem().deploy(credentials);
162
+ await deploy(liveSystem, credentials, {mode: 'wait'});
191
163
  ```
192
164
 
193
- The blueprint is registered with Fractal Cloud. The Automation Engine reconciles cloud resources to match the Live System definition.
165
+ `deploy` submits the Live System (create or update) to Fractal Cloud; the Automation Engine reconciles cloud resources to match it.
194
166
 
195
167
  ## Deployment modes
196
168
 
197
- `liveSystem.deploy()` supports two modes via an optional `DeployOptions` argument.
169
+ `deploy(liveSystem, credentials, options)` supports two modes.
198
170
 
199
171
  ### Fire and forget (default)
200
172
 
201
- Submits the live system to Fractal Cloud and returns immediately. Provisioning happens asynchronously. Errors are logged but not thrown. This is the default when no options are passed.
173
+ Submits the live system and returns immediately. Provisioning happens asynchronously. This is the default when no options are passed.
202
174
 
203
175
  ```typescript
204
- // Equivalent — both are fire-and-forget
205
- await liveSystem.deploy(credentials);
206
- await liveSystem.deploy(credentials, {mode: 'fire-and-forget'});
176
+ await deploy(liveSystem, credentials);
177
+ await deploy(liveSystem, credentials, {mode: 'fire-and-forget'}); // equivalent
207
178
  ```
208
179
 
209
180
  Best for: **applications, CLIs, scripts** where infrastructure deployment is a background concern.
210
181
 
211
182
  ### Wait for Active
212
183
 
213
- Submits the live system, then polls until all components reach `Active` status. Throws if deployment fails (`FailedMutation`, `Error`) or if the timeout is exceeded.
184
+ Submits, then polls until the live system reaches `Active`. Throws on terminal failure (`FailedMutation`, `Error`) or timeout.
214
185
 
215
186
  ```typescript
216
- await liveSystem.deploy(credentials, {
187
+ await deploy(liveSystem, credentials, {
217
188
  mode: 'wait',
218
189
  pollIntervalMs: 10_000, // check every 10 s (default: 5 s)
219
190
  timeoutMs: 900_000, // give up after 15 min (default: 10 min)
191
+ quiet: false, // set true to suppress wait-mode log lines
220
192
  });
221
193
  // reaches here only when the live system is fully Active
222
194
  ```
223
195
 
224
- Best for: **CI/CD pipelines** where the pipeline must not advance until infrastructure is fully provisioned.
196
+ Best for: **CI/CD pipelines** that must not advance until infrastructure is provisioned.
197
+
198
+ ### Destroy
199
+
200
+ ```typescript
201
+ import {destroy} from '@fractal_cloud/sdk';
202
+ await destroy(liveSystem, credentials);
203
+ ```
225
204
 
226
205
  ---
227
206
 
228
- ## Multi-provider support
207
+ ## Catalogue
229
208
 
230
- The same blueprint can be deployed on any supported provider. Live system files are short and only contain vendor-specific parameters all structural decisions (dependencies, traffic rules, security rules) stay in the blueprint.
209
+ The blueprint references the **Component** in the left column; a Live System selects any **Offer** beneath it. Offers with no provider are vendor-neutral self-hosted (run on any cluster).
231
210
 
232
- ### IaaS
211
+ ### NetworkAndCompute
233
212
 
234
- | Blueprint component | AWS | Azure | GCP | OCI | Hetzner | VMware | OpenShift | Aruba |
235
- |---------------------|-----|-------|-----|-----|---------|--------|-----------|-------|
236
- | `VirtualNetwork` | `AwsVpc` | `AzureVnet` | `GcpVpc` | `OciVcn` | `HetznerNetwork` | `VspherePortGroup` | — | `ArubaVpc` |
237
- | `Subnet` | `AwsSubnet` | `AzureSubnet` | `GcpSubnet` | `OciSubnet` | `HetznerSubnet` | `VsphereVlan` | — | `ArubaSubnet` |
238
- | `SecurityGroup` | `AwsSecurityGroup` | `AzureNsg` | `GcpFirewall` | `OciSecurityList` | `HetznerFirewall` | — | `OpenshiftSecurityGroup` | `ArubaSecurityGroup` |
239
- | `VirtualMachine` | `Ec2Instance` | `AzureVm` | `GcpVm` | `OciInstance` | `HetznerServer` | `VsphereVm` | `OpenshiftVm` | `ArubaCloudServer` |
213
+ | Component | AWS | Azure | GCP | OCI | Hetzner | VMware | RedHat (OpenShift) |
214
+ |---|---|---|---|---|---|---|---|
215
+ | `VirtualNetwork` | `AwsVpc` | `AzureVnet` | `GcpVpc` | `OciVcn` | `HetznerNetwork` | `VspherePortGroup` | — |
216
+ | `Subnet` | `AwsSubnet` | `AzureSubnet` | `GcpSubnet` | `OciSubnet` | `HetznerSubnet` | `VsphereVlan` | — |
217
+ | `SecurityGroup` | `AwsSecurityGroup` | `AzureNsg` | `GcpFirewall` | `OciSecurityList` | `HetznerFirewall` | — | `OpenshiftSecurityGroup` |
218
+ | `VirtualMachine` | `Ec2Instance` | `AzureVm` | `GcpVm` | `OciInstance` | `HetznerServer` | `VsphereVm` | `OpenshiftVm` |
219
+ | `ContainerPlatform` | `Eks` | `Aks` | `Gke` | — | — | — | — |
220
+ | `LoadBalancer` | `AwsLb` | `AzureLb` | `GcpGlb` | — | — | — | `OpenshiftService` |
240
221
 
241
- ### PaaS / CaaS
222
+ ### CustomWorkloads
242
223
 
243
- | Blueprint component | AWS | Azure | GCP | OCI | OpenShift | Aruba | CaaS |
244
- |---------------------|-----|-------|-----|-----|-----------|-------|------|
245
- | `ContainerPlatform` | `AwsEcsCluster` · `AwsEksCluster` | `AzureAksCluster` · `AzureContainerAppsEnvironment` | `GcpGkeCluster` | — | — | `ArubaKaaS` | — |
246
- | `Workload` | `AwsEcsTaskDefinition` + `AwsEcsService` | `AzureContainerInstance` · `AzureContainerApp` | `GcpCloudRunService` | `OciContainerInstance` | `OpenshiftWorkload` | — | `CaaSK8sWorkload` |
224
+ | Component | AWS | Azure | GCP | RedHat | Self-hosted |
225
+ |---|---|---|---|---|---|
226
+ | `Workload` | `EcsService` | `AzureContainerApp` | `CloudRun` | `OpenshiftWorkload` | `K8sWorkload` |
227
+ | `Function` | `AwsLambda` | `AzureFunction` | `GcpFunction` | | — |
247
228
 
248
229
  ### Storage
249
230
 
250
- | Blueprint component | AWS | Azure | GCP | Aruba | CaaS |
251
- |---------------------|-----|-------|-----|-------|------|
252
- | `FilesAndBlobs` | `AwsS3` | `AzureStorageAccount` · `AzureBlobContainer` · `AzureFileStorage` | `GcpCloudStorage` | `ArubaObjectStorageAccount` | — |
253
- | `RelationalDbms` | — | `AzurePostgreSqlDbms` · `AzureCosmosDbAccount` | `GcpPostgreSqlDbms` | `ArubaMySqlDbms` · `ArubaMsSqlDbms` | — |
254
- | `RelationalDatabase` | — | `AzurePostgreSqlDatabase` · `AzureCosmosDbPostgreSqlDatabase` | `GcpPostgreSqlDatabase` | — | — |
255
- | `DocumentDbms` | — | `AzureCosmosDbAccount` | `GcpFirestore` | — | — |
256
- | `ColumnOrientedDbms` | — | `AzureCosmosDbCassandra` | `GcpBigTable` | — | — |
257
- | `Search` | — | — | — | — | `Elastic` |
231
+ | Component | AWS | Azure | GCP | Aruba | RedHat | Self-hosted |
232
+ |---|---|---|---|---|---|---|
233
+ | `ObjectStorage` | `AwsS3` | `AzureBlob` | `GcsBucket` | | `OpenshiftPersistentVolume` | `MinIO` |
234
+ | `RelationalDbms` | — | `AzurePostgresDbms` | `GcpPostgresDbms` | `ArubaMySqlDbms` | | — |
235
+ | `RelationalDatabase` | — | `AzurePostgresDatabase` | `GcpPostgresDatabase` | | — | — |
258
236
 
259
- ### BigData
237
+ > `RelationalDatabase` components added under a DBMS via an operation are emitted by the **DBMS's own offer** in its vendor family — selecting `AzurePostgresDbms` makes its databases `AzurePostgresDatabase`. They are not independently offer-selected.
260
238
 
261
- | Blueprint component | AWS | Azure | GCP | Aruba | CaaS |
262
- |---------------------|-----|-------|-----|-------|------|
263
- | `DistributedDataProcessing` | `AwsDatabricks` | `AzureDatabricks` | `GcpDatabricks` | — | `CaaSSparkOperator` |
264
- | `ComputeCluster` | `AwsDatabricksCluster` | `AzureDatabricksCluster` | `GcpDatabricksCluster` | — | `CaaSSparkCluster` |
265
- | `DataProcessingJob` | `AwsDatabricksJob` | `AzureDatabricksJob` | `GcpDatabricksJob` | — | `CaaSSparkJob` |
266
- | `MlExperiment` | `AwsDatabricksMlflow` | `AzureDatabricksMlflow` | `GcpDatabricksMlflow` | — | `CaaSMlflow` |
267
- | `Datalake` | `AwsS3Datalake` | `AzureDatalake` | `GcpDatalake` | — | `CaaSMinioTenant` |
239
+ ### Messaging
268
240
 
269
- ### OpenShift-specific offers
241
+ | Component | Azure | GCP | Self-hosted |
242
+ |---|---|---|---|
243
+ | `Broker` | `AzureServiceBus` | `GcpPubSub` | `Kafka` |
244
+ | `MessagingEntity` | `AzureServiceBusTopic` | `GcpPubSubTopic` | `KafkaTopic` |
270
245
 
271
- | Offer | Type string | Blueprint equivalent |
272
- |-------|-------------|---------------------|
273
- | `OpenshiftService` | `NetworkAndCompute.CaaS.OpenshiftService` | Kubernetes Service + Route (standalone) |
274
- | `OpenshiftPersistentVolume` | `Storage.CaaS.OpenshiftPersistentVolume` | Persistent Volume Claim (standalone) |
246
+ ### BigData
275
247
 
276
- ### Aruba-specific offers
248
+ | Component | AWS | Azure | GCP | Self-hosted |
249
+ |---|---|---|---|---|
250
+ | `DistributedDataProcessing` | `AwsDatabricks` | `AzureDatabricks` | `GcpDatabricks` | — |
251
+ | `ComputeCluster` | `AwsDatabricksCluster` | `AzureDatabricksCluster` | `GcpDatabricksCluster` | `CaaSSparkCluster` |
252
+ | `DataProcessingJob` | `AwsDatabricksJob` | `AzureDatabricksJob` | `GcpDatabricksJob` | `CaaSSparkJob` |
253
+ | `MlExperiment` | `AwsDatabricksMlflow` | `AzureDatabricksMlflow` | `GcpDatabricksMlflow` | `CaaSMlflow` |
254
+ | `Datalake` | `AwsS3Datalake` | `AzureDatalake` | `GcpDatalake` | — |
277
255
 
278
- These offers have no cross-vendor blueprint equivalent and are used standalone alongside the satisfied components above.
256
+ ### APIManagement
279
257
 
280
- | Offer | Type string | Description |
281
- |-------|-------------|-------------|
282
- | `ArubaSshKeyPair` | `NetworkAndCompute.IaaS.ArubaSshKeyPair` | SSH key pair registered in the Aruba account |
283
- | `ArubaElasticIp` | `NetworkAndCompute.IaaS.ArubaElasticIp` | Reserved public IP for Aruba cloud servers |
284
- | `ArubaVpcPeering` | `NetworkAndCompute.IaaS.ArubaVpcPeering` | VPC-to-VPC peering connection |
285
- | `ArubaVpnTunnel` | `NetworkAndCompute.IaaS.ArubaVpnTunnel` | Site-to-site VPN tunnel |
286
- | `ArubaContainerRegistry` | `NetworkAndCompute.PaaS.ArubaContainerRegistry` | Managed container image registry |
287
- | `ArubaBlockStorage` | `Storage.IaaS.ArubaBlockStorage` | Block storage volume attachable to a cloud server |
258
+ | Component | AWS | Azure | GCP | Self-hosted |
259
+ |---|---|---|---|---|
260
+ | `ApiGateway` | `AwsCloudFront` | `AzureApiManagement` | `GcpApiGateway` | `Ambassador` · `Traefik` |
288
261
 
289
- ## Custom Aria Components
262
+ ### Observability (self-hosted, CaaS)
290
263
 
291
- Enterprise customers running custom aria agents can extend the SDK with their own infrastructure domains, service delivery models, and component types — no fork required.
264
+ | Component | Offer |
265
+ |---|---|
266
+ | `Monitoring` | `Prometheus` |
267
+ | `Tracing` | `Jaeger` |
268
+ | `Logging` | `ObservabilityElastic` |
292
269
 
293
- ### Defining custom component types
270
+ ### Security
294
271
 
295
- Use `Custom.blueprint()` and `Custom.offer()` to create reusable factories. Each factory stamps out components of a specific type, just like the built-in helpers (`VirtualNetwork`, `AwsVpc`, etc.).
272
+ | Component | AWS | Self-hosted |
273
+ |---|---|---|
274
+ | `ServiceMesh` | — | `Ocelot` |
275
+ | `IdentityProvider` | `Cognito` | `Keycloak` |
296
276
 
297
- ```typescript
298
- import {
299
- Custom, Fractal, LiveSystem, ServiceDeliveryModel,
300
- } from '@fractal_cloud/sdk';
277
+ ## Extending the catalogue
301
278
 
302
- // ── 1. Define factories (once per type, reusable) ─────────────────────────
279
+ Both Components and Offers are plain values you can add your own without forking.
303
280
 
304
- const TimeSeriesStore = Custom.blueprint({
305
- domain: 'Analytics', // any string — not limited to built-in domains
306
- serviceDeliveryModel: ServiceDeliveryModel.PaaS,
307
- name: 'TimeSeriesStore', // must be PascalCase
308
- });
309
-
310
- const InfluxDb = Custom.offer({
311
- domain: 'Analytics',
312
- serviceDeliveryModel: ServiceDeliveryModel.PaaS,
313
- name: 'InfluxDb',
314
- provider: 'CustomProvider', // any string — not limited to built-in providers
315
- });
316
- ```
281
+ ### Add a new Offer (including a new vendor)
317
282
 
318
- ### Creating blueprint components (cloud-agnostic)
283
+ `defineOffer` returns an offer constructor. Declare which Component it `satisfies`, its 3-part `offerType`, its `deliveryModel`, an optional `provider` (one of the `Provider` vendors; omit for vendor-neutral self-hosted), and a config type for vendor knobs. **Every existing Fractal can select it automatically** — no blueprint changes.
319
284
 
320
285
  ```typescript
321
- // ── 2. Blueprint — what the architecture needs ────────────────────────────
322
-
323
- const metricsDb = TimeSeriesStore.create({
324
- id: 'metrics-db',
325
- version: { major: 1, minor: 0, patch: 0 },
326
- displayName: 'Metrics Database',
327
- parameters: { retentionDays: '90', replicationFactor: '3' },
286
+ import {defineOffer} from '@fractal_cloud/sdk';
287
+
288
+ // A self-hosted object store satisfying the built-in Storage.ObjectStorage
289
+ // Component. Vendor-neutral, so `provider` is omitted.
290
+ const Ceph = defineOffer<'Storage.ObjectStorage', {storageClass?: string}>({
291
+ satisfies: 'Storage.ObjectStorage',
292
+ offerType: 'Storage.CaaS.Ceph',
293
+ deliveryModel: 'CaaS',
328
294
  });
329
- ```
330
-
331
- ### Satisfying with a custom offer (vendor-specific)
332
-
333
- The `satisfy()` method works exactly like built-in offers: it locks the blueprint's id, version, displayName, description, dependencies, links, and parameters, then exposes only vendor-specific parameter setters.
334
-
335
- ```typescript
336
- // ── 3. Live system — how the architecture is provisioned ──────────────────
337
-
338
- const influx = InfluxDb.satisfy(metricsDb)
339
- .withParameter('bucket', 'metrics')
340
- .withParameter('orgId', 'my-org')
341
- .build();
342
- ```
343
295
 
344
- ### Standalone creation (without a blueprint)
345
-
346
- If you don't need the blueprint-satisfy flow, create live system components directly:
347
-
348
- ```typescript
349
- const influx = InfluxDb.create({
350
- id: 'metrics-db',
351
- version: { major: 1, minor: 0, patch: 0 },
352
- displayName: 'Metrics Database',
353
- parameters: { bucket: 'metrics', orgId: 'my-org' },
296
+ // Select it for any ObjectStorage slot, just like a built-in offer:
297
+ fractal.specialize().toLiveSystem({
298
+ name: 'acme-prod',
299
+ environment,
300
+ select: {uploads: Ceph({storageClass: 'rbd'})},
354
301
  });
355
302
  ```
356
303
 
357
- ### Using the fluent builder
358
-
359
- For advanced scenarios (dependencies, links, conditional params), use the builder API:
360
-
361
- ```typescript
362
- const metricsDb = TimeSeriesStore.getBuilder()
363
- .withId('metrics-db')
364
- .withVersion(1, 0, 0)
365
- .withDisplayName('Metrics Database')
366
- .withParameter('retentionDays', '90')
367
- .withDependencies([{ id: someOtherComponent.id }])
368
- .withLinks([{ id: anotherComponent.id, parameters: linkParams }])
369
- .build();
370
- ```
371
-
372
- ### Deploying custom components
373
-
374
- Custom components integrate seamlessly with the standard deploy flow:
304
+ By default an offer emits one live component merging the blueprint's neutral params with its vendor config. Pass an `instantiate(ctx, config)` to emit a custom set of live components (e.g. a parent plus one child per `ctx.children` entry).
375
305
 
376
- ```typescript
377
- // ── 4. Deploy as usual ────────────────────────────────────────────────────
378
-
379
- const fractal = Fractal.getBuilder()
380
- .withId(fractalId)
381
- .withComponents([metricsDb])
382
- .build();
383
-
384
- const liveSystem = LiveSystem.getBuilder()
385
- .withId(liveSystemId)
386
- .withFractalId(fractal.id)
387
- .withGenericProvider('CustomProvider')
388
- .withEnvironment(environment)
389
- .withComponent(influx)
390
- .build();
391
-
392
- await fractal.deploy(credentials);
393
- await liveSystem.deploy(credentials, { mode: 'wait' });
394
- ```
395
-
396
- ### Mixing custom and built-in components
306
+ ### Add a new abstract Component
397
307
 
398
- Custom components can coexist with built-in ones in the same Fractal:
308
+ Author a Component factory on the core authoring primitives (`newNode`, `guardrail`, `addDependency`). Each `.withXxx()` setter records a neutral parameter and locks it as a guardrail.
399
309
 
400
310
  ```typescript
401
- import { VirtualNetwork, Subnet, Custom, ServiceDeliveryModel } from '@fractal_cloud/sdk';
402
-
403
- const MyCache = Custom.blueprint({
404
- domain: 'Performance',
405
- serviceDeliveryModel: ServiceDeliveryModel.PaaS,
406
- name: 'DistributedCache',
311
+ import {ComponentNode, NodeState, newNode, guardrail} from '@fractal_cloud/sdk';
312
+
313
+ type TimeSeriesNode<Id extends string = string> = ComponentNode<Id, 'Analytics.TimeSeries'> & {
314
+ withRetentionDays: (v: number) => TimeSeriesNode<Id>;
315
+ };
316
+ const node = <Id extends string>(s: NodeState): TimeSeriesNode<Id> => ({
317
+ state: s,
318
+ withRetentionDays: v => node<Id>(guardrail(s, 'retentionDays', v)),
407
319
  });
408
-
409
- const network = VirtualNetwork.create({ /* ... */ })
410
- .withSubnets([
411
- Subnet.create({ /* ... */ }),
412
- ]);
413
-
414
- const cache = MyCache.create({
415
- id: 'app-cache',
416
- version: { major: 1, minor: 0, patch: 0 },
417
- displayName: 'Application Cache',
418
- parameters: { maxMemoryMb: '4096', evictionPolicy: 'lru' },
419
- dependencies: [{ id: network.vpc.id }], // depend on built-in components
420
- });
421
-
422
- const fractal = Fractal.getBuilder()
423
- .withId(fractalId)
424
- .withComponents([...network.components, cache])
425
- .build();
320
+ export const TimeSeries = <const Id extends string>(cfg: {id: Id}): TimeSeriesNode<Id> =>
321
+ node<Id>(newNode(cfg.id, 'Analytics.TimeSeries'));
426
322
  ```
427
323
 
428
- ### API reference
429
-
430
- | Function | Returns | Description |
431
- |----------|---------|-------------|
432
- | `Custom.blueprint(config)` | `CustomBlueprintFactory` | Define a reusable blueprint component type |
433
- | `Custom.offer(config)` | `CustomOfferFactory` | Define a reusable live system offer type |
434
-
435
- **`CustomBlueprintFactory`**
436
-
437
- | Method | Returns | Description |
438
- |--------|---------|-------------|
439
- | `.create(config)` | `BlueprintComponent` | Create a component with id, version, displayName, parameters, dependencies, links |
440
- | `.getBuilder()` | `CustomBlueprintBuilder` | Fluent builder with `withParameter(key, value)`, `withDependencies()`, `withLinks()` |
441
- | `.typeString` | `string` | The full type string (e.g. `"Analytics.PaaS.TimeSeriesStore"`) |
442
-
443
- **`CustomOfferFactory`**
444
-
445
- | Method | Returns | Description |
446
- |--------|---------|-------------|
447
- | `.create(config)` | `LiveSystemComponent` | Create a live system component directly |
448
- | `.getBuilder()` | `CustomOfferBuilder` | Fluent builder with full access to all fields |
449
- | `.satisfy(blueprint)` | `CustomSatisfiedBuilder` | Lock structural fields from blueprint; only `withParameter()`/`withParameters()` exposed |
450
- | `.typeString` | `string` | The full type string (e.g. `"Analytics.PaaS.InfluxDb"`) |
451
-
452
- **`CustomSatisfiedBuilder`** (returned by `satisfy()`)
453
-
454
- | Method | Returns | Description |
455
- |--------|---------|-------------|
456
- | `.withParameter(key, value)` | `CustomSatisfiedBuilder` | Add a single vendor-specific parameter |
457
- | `.withParameters(record)` | `CustomSatisfiedBuilder` | Add multiple vendor-specific parameters at once |
458
- | `.build()` | `LiveSystemComponent` | Build the final component |
459
-
460
- ### Validation
461
-
462
- - **Component name** must be PascalCase (e.g. `TimeSeriesStore`, `InfluxDb`). Validated eagerly when the factory is created — a `SyntaxError` is thrown immediately for invalid names.
463
- - **Component id** must be kebab-case (e.g. `metrics-db`). Each segment must start with a letter.
464
- - **Domain and provider** accept any string. Built-in values (`InfrastructureDomain.Storage`, `'AWS'`, etc.) provide autocomplete but are not enforced.
465
- - Custom domains and providers must be registered with the Fractal Cloud platform for deployment to succeed.
466
-
467
- ---
324
+ Then write offers that `satisfies: 'Analytics.TimeSeries'`. Custom domains, components, and vendors must be registered with the Fractal Cloud platform for deployment to succeed.
468
325
 
469
326
  ## Samples
470
327
 
471
- The [sample repository](https://github.com/Fractal-Cloud/fractal-ts-sdk-samples) contains ready-to-run examples:
472
-
473
- | Sample | Providers | Description |
474
- |--------|-----------|-------------|
475
- | `basic_iaas` | AWS · Azure · GCP · OCI · Hetzner | VPC + Subnet + Security Group + two VMs |
476
- | `basic_container_platform` | AWS · Azure · GCP | VPC + Subnet + SG + container platform + two workloads |
477
- | `basic_cicd` | AWS | CI/CD pipeline deployment with wait mode |
478
- | `basic_storage` | AWS · Azure · GCP | PostgreSQL DBMS + Database + object storage |
479
- | `basic_messaging` | Azure · GCP | Message broker + two topics |
480
- | `basic_big_data` | AWS · Azure · GCP | Databricks workspace + cluster + job + MLflow |
481
- | `basic_api_management` | AWS · Azure · GCP | API Gateway |
482
- | `basic_observability` | CaaS | Monitoring + tracing + logging (Prometheus, Jaeger, Elastic) |
483
- | `basic_security` | CaaS | Service mesh (Ocelot) |
328
+ The [sample repository](https://github.com/Fractal-Cloud/fractal-ts-sdk-samples) contains ready-to-run examples consumed via the `@fractal_cloud/sdk/model` subpath. `basic_storage` is the canonical reference.
484
329
 
485
330
  ## Architecture
486
331
 
487
- ```
488
- src/
489
- custom/ # Custom aria component factories (Custom.blueprint, Custom.offer)
490
- fractal/ # Cloud-agnostic blueprint helpers
491
- component/
492
- network_and_compute/ # VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform
493
- custom_workloads/ # Workload
494
- storage/ # FilesAndBlobs, RelationalDbms, DocumentDbms, Search, etc.
495
- messaging/ # Broker, Entity
496
- big_data/ # DistributedDataProcessing, ComputeCluster, DataProcessingJob, etc.
497
- api_management/ # ApiGateway
498
- observability/ # Monitoring, Tracing, Logging
499
- security/ # ServiceMesh
500
- live_system/ # Provider-specific helpers
501
- component/
502
- network_and_compute/ # AWS, Azure, GCP, OCI, Hetzner, VMware, Aruba (IaaS) + OpenShift (CaaS) + AWS/Azure/GCP/Aruba (PaaS)
503
- custom_workloads/ # OpenShift, generic Kubernetes (CaaS)
504
- storage/ # AWS S3, Azure Storage/CosmosDB/PostgreSQL, GCP Storage/Firestore/BigTable, Aruba MySQL/MsSQL/Object/Block, OpenShift PV
505
- messaging/ # Azure ServiceBus/EventHub, GCP PubSub, CaaS Kafka
506
- big_data/ # AWS/Azure/GCP Databricks + Datalake, Aruba S3 Datalake, CaaS Spark (Operator/Cluster/Job/MLflow)
507
- api_management/ # AWS CloudFront, Azure API Management, GCP API Gateway, CaaS Ambassador/Traefik
508
- observability/ # CaaS Prometheus, Jaeger, Elastic
509
- security/ # CaaS Ocelot
510
- ```
511
-
512
- ## Debug logging
513
-
514
- Set `FRACTAL_DEBUG=true` to log every outbound HTTP request and every inbound response to stdout. This covers all API calls made by `fractal.deploy()`, `liveSystem.deploy()`, `fractal.destroy()`, and `liveSystem.destroy()`.
332
+ The package root and the `./model` subpath export the same surface.
515
333
 
516
334
  ```
517
- [2026-03-11T14:23:00Z] DEBUG → GET https://api.fractal.cloud/livesystems/my-ls
518
- [2026-03-11T14:23:00Z] DEBUG 404 GET https://api.fractal.cloud/livesystems/my-ls body=null
519
- [2026-03-11T14:23:00Z] DEBUG POST https://api.fractal.cloud/livesystems body={"liveSystemId":"..."}
520
- [2026-03-11T14:23:00Z] DEBUG 201 POST https://api.fractal.cloud/livesystems body=null
335
+ src/model/
336
+ core.ts # Engine: createFractal, defineOffer, ComponentNode, typed Selection,
337
+ # guardrails/locking, links, child components, fluent .specialize()
338
+ service.ts # deploy / destroy a LiveSystem (HTTP + poll + wait-mode log contract)
339
+ index.ts # Public barrel
340
+ components/ # Abstract Component factories (Level 1, vendor-agnostic)
341
+ network_and_compute.ts # VirtualNetwork, Subnet, SecurityGroup, VirtualMachine,
342
+ # ContainerPlatform, LoadBalancer
343
+ custom_workloads.ts # Workload, Function
344
+ storage.ts # ObjectStorage, RelationalDbms, RelationalDatabase
345
+ messaging.ts # Broker, MessagingEntity
346
+ big_data.ts # DistributedDataProcessing, ComputeCluster, DataProcessingJob,
347
+ # MlExperiment, Datalake
348
+ api_management.ts # ApiGateway
349
+ observability.ts # Monitoring, Tracing, Logging
350
+ security.ts # ServiceMesh, IdentityProvider
351
+ offers/ # Concrete Offers (Level 3) declaring what Component they satisfy
352
+ <domain>.ts # one file per domain, mirroring components/
353
+ *.test.ts # vitest specs — the executable regression suite
521
354
  ```
522
355
 
523
- - Authentication headers (`X-ClientID`, `X-ClientSecret`) are never logged.
524
- - Request and response bodies are truncated to 2 000 characters.
525
- - Both success and error responses are logged.
526
- - Works in all deploy modes (`fire-and-forget` and `wait`).
527
-
528
- ```bash
529
- FRACTAL_DEBUG=true node build/src/index.js
530
- ```
356
+ See [`docs/fractal-model.md`](docs/fractal-model.md) for the locked model specification.
531
357
 
532
358
  ## Contributing and feedback
533
359
 
@@ -552,3 +378,4 @@ Made with ❤️ by the Fractal Cloud team.
552
378
  [codecov-url]: https://codecov.io/gh/Fractal-Cloud/fractal-ts-sdk
553
379
  [snyk-image]: https://snyk.io/test/github/Fractal-Cloud/fractal-ts-sdk/badge.svg
554
380
  [snyk-url]: https://snyk.io/test/github/Fractal-Cloud/fractal-ts-sdk
381
+ </content>