@semiont/cli 0.1.0-build.2

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.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import * as cdk from 'aws-cdk-lib';
3
+ import * as path from 'path';
4
+ import { SemiontDataStack } from './data-stack';
5
+ import { SemiontAppStack } from './app-stack';
6
+
7
+ const app = new cdk.App();
8
+
9
+ // Get configuration from context and environment
10
+ const stackType = app.node.tryGetContext('stack-type') || 'all';
11
+ const environment = app.node.tryGetContext('environment') || process.env.SEMIONT_ENV || 'production';
12
+
13
+ // Load configurations using absolute paths from the project root
14
+ // Use process.cwd() which is the project root when CDK executes
15
+ const projectRoot = process.cwd();
16
+ const semiontConfig = require(path.join(projectRoot, 'semiont.json'));
17
+ const envConfig = require(path.join(projectRoot, 'environments', `${environment}.json`));
18
+
19
+ // Stack properties
20
+ const stackProps = {
21
+ env: {
22
+ account: envConfig.aws?.accountId || process.env.CDK_DEFAULT_ACCOUNT,
23
+ region: envConfig.aws?.region || process.env.CDK_DEFAULT_REGION || 'us-east-1',
24
+ },
25
+ synthesizer: new cdk.DefaultStackSynthesizer({
26
+ qualifier: 'hnb659fds'
27
+ })
28
+ };
29
+
30
+ // Set CDK context with configuration values
31
+ app.node.setContext('environment', environment);
32
+ app.node.setContext('siteName', semiontConfig.site?.siteName || 'Semiont');
33
+ app.node.setContext('domain', semiontConfig.site?.domain || 'example.com');
34
+ app.node.setContext('rootDomain', semiontConfig.site?.domain?.split('.').slice(-2).join('.') || 'example.com');
35
+ app.node.setContext('adminEmail', semiontConfig.site?.adminEmail || 'admin@example.com');
36
+ app.node.setContext('oauthAllowedDomains', semiontConfig.site?.oauthAllowedDomains || ['example.com']);
37
+ app.node.setContext('databaseName', semiontConfig.services?.database?.name || 'semiont');
38
+ app.node.setContext('certificateArn', envConfig.aws?.certificateArn);
39
+ app.node.setContext('hostedZoneId', envConfig.aws?.hostedZoneId);
40
+ app.node.setContext('backendImageUri', envConfig.services?.backend?.image);
41
+ app.node.setContext('frontendImageUri', envConfig.services?.frontend?.image);
42
+ app.node.setContext('nodeEnv', envConfig.env?.NODE_ENV || 'production');
43
+
44
+ // Create stacks based on stack-type context
45
+ if (stackType === 'data' || stackType === 'all') {
46
+ new SemiontDataStack(app, 'SemiontDataStack', stackProps);
47
+ }
48
+
49
+ if (stackType === 'app' || stackType === 'all') {
50
+ // App stack will import resources from data stack via CloudFormation exports
51
+ new SemiontAppStack(app, 'SemiontAppStack', stackProps);
52
+ }
53
+
54
+ app.synth();
@@ -0,0 +1,416 @@
1
+ import * as cdk from 'aws-cdk-lib';
2
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
3
+ import * as rds from 'aws-cdk-lib/aws-rds';
4
+ import * as efs from 'aws-cdk-lib/aws-efs';
5
+ import * as neptune from 'aws-cdk-lib/aws-neptune';
6
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
7
+ import * as iam from 'aws-cdk-lib/aws-iam';
8
+ import { Construct } from 'constructs';
9
+
10
+ export class SemiontDataStack extends cdk.Stack {
11
+ public readonly vpc: ec2.Vpc;
12
+ public readonly fileSystem: efs.FileSystem;
13
+ public readonly database: rds.DatabaseInstance;
14
+ public readonly neptuneCluster: neptune.CfnDBCluster;
15
+ public readonly neptuneInstance: neptune.CfnDBInstance;
16
+ public readonly dbCredentials: secretsmanager.Secret;
17
+ public readonly appSecrets: secretsmanager.Secret;
18
+ public readonly jwtSecret: secretsmanager.Secret;
19
+ public readonly adminPassword: secretsmanager.Secret;
20
+ public readonly googleOAuth: secretsmanager.Secret;
21
+ public readonly githubOAuth: secretsmanager.Secret;
22
+ public readonly adminEmails: secretsmanager.Secret;
23
+ public readonly dbSecurityGroup: ec2.SecurityGroup;
24
+ public readonly neptuneSecurityGroup: ec2.SecurityGroup;
25
+ public readonly ecsSecurityGroup: ec2.SecurityGroup;
26
+ public readonly albSecurityGroup: ec2.SecurityGroup;
27
+
28
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
29
+ super(scope, id, props);
30
+
31
+ // VPC with proper subnet isolation
32
+ this.vpc = new ec2.Vpc(this, 'SemiontVpc', {
33
+ maxAzs: 2,
34
+ natGateways: 2,
35
+ subnetConfiguration: [
36
+ {
37
+ cidrMask: 24,
38
+ name: 'public',
39
+ subnetType: ec2.SubnetType.PUBLIC,
40
+ },
41
+ {
42
+ cidrMask: 24,
43
+ name: 'private',
44
+ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
45
+ },
46
+ {
47
+ cidrMask: 24,
48
+ name: 'database',
49
+ subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
50
+ },
51
+ ],
52
+ });
53
+
54
+ // Database credentials in Secrets Manager
55
+ this.dbCredentials = new secretsmanager.Secret(this, 'DatabaseCredentials', {
56
+ generateSecretString: {
57
+ secretStringTemplate: JSON.stringify({ username: 'semiont' }),
58
+ generateStringKey: 'password',
59
+ excludeCharacters: '"@/\\',
60
+ },
61
+ });
62
+
63
+ // Application secrets (contains both session and NextAuth secrets)
64
+ this.appSecrets = new secretsmanager.Secret(this, 'AppSecrets', {
65
+ description: 'Application secrets for Semiont (session and NextAuth)',
66
+ secretObjectValue: {
67
+ sessionSecret: cdk.SecretValue.unsafePlainText('REPLACE_WITH_SESSION_SECRET_64_CHARS'),
68
+ nextAuthSecret: cdk.SecretValue.unsafePlainText('REPLACE_WITH_NEXTAUTH_SECRET_64_CHARS'),
69
+ },
70
+ });
71
+
72
+ this.jwtSecret = new secretsmanager.Secret(this, 'JwtSecret', {
73
+ generateSecretString: {
74
+ secretStringTemplate: JSON.stringify({}),
75
+ generateStringKey: 'jwtSecret',
76
+ passwordLength: 64,
77
+ },
78
+ });
79
+
80
+ // Admin password in Secrets Manager
81
+ this.adminPassword = new secretsmanager.Secret(this, 'AdminPassword', {
82
+ generateSecretString: {
83
+ secretStringTemplate: JSON.stringify({}),
84
+ generateStringKey: 'password',
85
+ passwordLength: 20,
86
+ excludeCharacters: '"@/\\`\'',
87
+ },
88
+ });
89
+
90
+ // OAuth credentials for Google
91
+ this.googleOAuth = new secretsmanager.Secret(this, 'GoogleOAuthCredentials', {
92
+ description: 'Google OAuth client credentials for Semiont',
93
+ secretObjectValue: {
94
+ clientId: cdk.SecretValue.unsafePlainText('REPLACE_WITH_GOOGLE_CLIENT_ID'),
95
+ clientSecret: cdk.SecretValue.unsafePlainText('REPLACE_WITH_GOOGLE_CLIENT_SECRET'),
96
+ },
97
+ });
98
+
99
+ // OAuth credentials for GitHub (temporary - to maintain CloudFormation exports)
100
+ this.githubOAuth = new secretsmanager.Secret(this, 'GitHubOAuthCredentials', {
101
+ description: 'GitHub OAuth app credentials for Semiont (unused)',
102
+ secretObjectValue: {
103
+ clientId: cdk.SecretValue.unsafePlainText('UNUSED'),
104
+ clientSecret: cdk.SecretValue.unsafePlainText('UNUSED'),
105
+ },
106
+ });
107
+
108
+ // Admin users list
109
+ const adminEmail = this.node.tryGetContext('adminEmail') || 'admin@example.com';
110
+ this.adminEmails = new secretsmanager.Secret(this, 'AdminEmails', {
111
+ description: 'Comma-separated list of admin email addresses',
112
+ secretObjectValue: {
113
+ emails: cdk.SecretValue.unsafePlainText(adminEmail),
114
+ },
115
+ });
116
+
117
+ // Security Groups
118
+ this.dbSecurityGroup = new ec2.SecurityGroup(this, 'DatabaseSecurityGroup', {
119
+ vpc: this.vpc,
120
+ description: 'Security group for Semiont database',
121
+ allowAllOutbound: false, // Database should not initiate outbound connections
122
+ });
123
+
124
+ this.ecsSecurityGroup = new ec2.SecurityGroup(this, 'EcsSecurityGroup', {
125
+ vpc: this.vpc,
126
+ description: 'Security group for Semiont ECS tasks',
127
+ // allowAllOutbound: true (default) - needed for internet access, docker pulls, etc.
128
+ });
129
+
130
+ this.albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSecurityGroup', {
131
+ vpc: this.vpc,
132
+ description: 'Security group for Application Load Balancer',
133
+ });
134
+
135
+ // Configure security group rules
136
+ this.dbSecurityGroup.addIngressRule(
137
+ this.ecsSecurityGroup,
138
+ ec2.Port.tcp(5432),
139
+ 'Allow ECS to access PostgreSQL'
140
+ );
141
+
142
+ this.albSecurityGroup.addIngressRule(
143
+ ec2.Peer.anyIpv4(),
144
+ ec2.Port.tcp(80),
145
+ 'Allow HTTP'
146
+ );
147
+ this.albSecurityGroup.addIngressRule(
148
+ ec2.Peer.anyIpv4(),
149
+ ec2.Port.tcp(443),
150
+ 'Allow HTTPS'
151
+ );
152
+
153
+ this.ecsSecurityGroup.addIngressRule(
154
+ this.albSecurityGroup,
155
+ ec2.Port.tcp(80),
156
+ 'Allow ALB to reach ECS'
157
+ );
158
+
159
+ // PostgreSQL RDS with encryption
160
+ this.database = new rds.DatabaseInstance(this, 'SemiontDatabase', {
161
+ engine: rds.DatabaseInstanceEngine.postgres({
162
+ version: rds.PostgresEngineVersion.VER_15,
163
+ }),
164
+ instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
165
+ credentials: rds.Credentials.fromSecret(this.dbCredentials),
166
+ vpc: this.vpc,
167
+ vpcSubnets: {
168
+ subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
169
+ },
170
+ securityGroups: [this.dbSecurityGroup],
171
+ databaseName: this.node.tryGetContext('databaseName') || 'semiont',
172
+ storageEncrypted: true,
173
+ backupRetention: cdk.Duration.days(7),
174
+ deletionProtection: true,
175
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
176
+ multiAz: false,
177
+ allowMajorVersionUpgrade: false,
178
+ autoMinorVersionUpgrade: true,
179
+ deleteAutomatedBackups: false,
180
+ });
181
+
182
+ // EFS for persistent file storage
183
+ this.fileSystem = new efs.FileSystem(this, 'SemiontEFS', {
184
+ vpc: this.vpc,
185
+ lifecyclePolicy: efs.LifecyclePolicy.AFTER_30_DAYS,
186
+ performanceMode: efs.PerformanceMode.GENERAL_PURPOSE,
187
+ throughputMode: efs.ThroughputMode.BURSTING,
188
+ encrypted: true,
189
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
190
+ });
191
+
192
+ // Allow ECS to access EFS
193
+ this.fileSystem.connections.allowDefaultPortFrom(this.ecsSecurityGroup, 'Allow ECS to EFS');
194
+
195
+ // Neptune Graph Database (required for document relationships)
196
+ // Neptune subnet group
197
+ const neptuneSubnetGroup = new neptune.CfnDBSubnetGroup(this, 'NeptuneSubnetGroup', {
198
+ dbSubnetGroupDescription: 'Subnet group for Neptune graph database',
199
+ subnetIds: this.vpc.privateSubnets.map(subnet => subnet.subnetId),
200
+ dbSubnetGroupName: `${this.stackName}-neptune-subnet-group`,
201
+ });
202
+
203
+ // Neptune security group
204
+ this.neptuneSecurityGroup = new ec2.SecurityGroup(this, 'NeptuneSecurityGroup', {
205
+ vpc: this.vpc,
206
+ description: 'Security group for Neptune graph database',
207
+ allowAllOutbound: false,
208
+ });
209
+
210
+ // Allow ECS to access Neptune
211
+ this.neptuneSecurityGroup.addIngressRule(
212
+ this.ecsSecurityGroup,
213
+ ec2.Port.tcp(8182),
214
+ 'Allow ECS to access Neptune'
215
+ );
216
+
217
+ // Neptune parameter group for optimal performance
218
+ const neptuneParameterGroup = new neptune.CfnDBParameterGroup(this, 'NeptuneParameterGroup', {
219
+ family: 'neptune1.3',
220
+ parameters: {
221
+ 'neptune_enable_audit_log': '0',
222
+ 'neptune_query_timeout': '120000',
223
+ },
224
+ description: 'Neptune parameter group for Semiont',
225
+ });
226
+
227
+ // Neptune cluster
228
+ this.neptuneCluster = new neptune.CfnDBCluster(this, 'NeptuneCluster', {
229
+ dbSubnetGroupName: neptuneSubnetGroup.dbSubnetGroupName,
230
+ vpcSecurityGroupIds: [this.neptuneSecurityGroup.securityGroupId],
231
+ dbClusterParameterGroupName: neptuneParameterGroup.ref,
232
+ engineVersion: '1.3.0.0',
233
+ storageEncrypted: true,
234
+ backupRetentionPeriod: 7,
235
+ preferredBackupWindow: '03:00-04:00',
236
+ preferredMaintenanceWindow: 'sun:04:00-sun:05:00',
237
+ deletionProtection: true,
238
+ iamDatabaseAuthenticationEnabled: true,
239
+ tags: [
240
+ { key: 'Application', value: 'Semiont' },
241
+ { key: 'Component', value: 'GraphDatabase' },
242
+ { key: 'Environment', value: this.node.tryGetContext('environment') || 'production' },
243
+ ],
244
+ });
245
+
246
+ // Neptune instance (t4g.medium is ARM-based and ~7% cheaper than t3.medium)
247
+ this.neptuneInstance = new neptune.CfnDBInstance(this, 'NeptuneInstance', {
248
+ dbInstanceClass: 'db.t4g.medium',
249
+ dbClusterIdentifier: this.neptuneCluster.ref,
250
+ dbSubnetGroupName: neptuneSubnetGroup.dbSubnetGroupName,
251
+ });
252
+
253
+ // Outputs for Neptune
254
+ new cdk.CfnOutput(this, 'NeptuneClusterEndpoint', {
255
+ value: this.neptuneCluster.attrEndpoint,
256
+ description: 'Neptune Cluster Endpoint',
257
+ exportName: `${this.stackName}-NeptuneClusterEndpoint`,
258
+ });
259
+
260
+ new cdk.CfnOutput(this, 'NeptuneClusterId', {
261
+ value: this.neptuneCluster.attrClusterResourceId,
262
+ description: 'Neptune Cluster ID',
263
+ exportName: `${this.stackName}-NeptuneClusterId`,
264
+ });
265
+
266
+ new cdk.CfnOutput(this, 'NeptuneReadEndpoint', {
267
+ value: this.neptuneCluster.attrReadEndpoint,
268
+ description: 'Neptune Read Endpoint',
269
+ exportName: `${this.stackName}-NeptuneReadEndpoint`,
270
+ });
271
+
272
+ new cdk.CfnOutput(this, 'NeptunePort', {
273
+ value: this.neptuneCluster.attrPort,
274
+ description: 'Neptune Port',
275
+ exportName: `${this.stackName}-NeptunePort`,
276
+ });
277
+
278
+ new cdk.CfnOutput(this, 'NeptuneSecurityGroupId', {
279
+ value: this.neptuneSecurityGroup.securityGroupId,
280
+ description: 'Neptune Security Group ID',
281
+ exportName: `${this.stackName}-NeptuneSecurityGroupId`,
282
+ });
283
+
284
+ // Add explicit EFS filesystem policy to allow access
285
+ this.fileSystem.addToResourcePolicy(
286
+ new iam.PolicyStatement({
287
+ sid: 'AllowClientAccess',
288
+ effect: iam.Effect.ALLOW,
289
+ principals: [new iam.AnyPrincipal()],
290
+ actions: [
291
+ 'elasticfilesystem:ClientMount',
292
+ 'elasticfilesystem:ClientWrite',
293
+ 'elasticfilesystem:ClientRootAccess',
294
+ ],
295
+ resources: ['*'],
296
+ conditions: {
297
+ Bool: {
298
+ 'aws:SecureTransport': 'false', // Allow non-TLS connections
299
+ },
300
+ },
301
+ })
302
+ );
303
+
304
+ // Export outputs for cross-stack references
305
+ new cdk.CfnOutput(this, 'VpcId', {
306
+ value: this.vpc.vpcId,
307
+ description: 'VPC ID',
308
+ exportName: `${this.stackName}-VpcId`,
309
+ });
310
+
311
+ // Export subnet IDs for VPC import
312
+ new cdk.CfnOutput(this, 'PublicSubnet1Id', {
313
+ value: this.vpc.publicSubnets[0].subnetId,
314
+ description: 'Public Subnet 1 ID',
315
+ exportName: `${this.stackName}-PublicSubnet1Id`,
316
+ });
317
+
318
+ new cdk.CfnOutput(this, 'PublicSubnet2Id', {
319
+ value: this.vpc.publicSubnets[1].subnetId,
320
+ description: 'Public Subnet 2 ID',
321
+ exportName: `${this.stackName}-PublicSubnet2Id`,
322
+ });
323
+
324
+ new cdk.CfnOutput(this, 'PrivateSubnet1Id', {
325
+ value: this.vpc.privateSubnets[0].subnetId,
326
+ description: 'Private Subnet 1 ID',
327
+ exportName: `${this.stackName}-PrivateSubnet1Id`,
328
+ });
329
+
330
+ new cdk.CfnOutput(this, 'PrivateSubnet2Id', {
331
+ value: this.vpc.privateSubnets[1].subnetId,
332
+ description: 'Private Subnet 2 ID',
333
+ exportName: `${this.stackName}-PrivateSubnet2Id`,
334
+ });
335
+
336
+ new cdk.CfnOutput(this, 'DatabaseEndpoint', {
337
+ value: this.database.instanceEndpoint.hostname,
338
+ description: 'RDS Database Endpoint',
339
+ exportName: `${this.stackName}-DatabaseEndpoint`,
340
+ });
341
+
342
+ new cdk.CfnOutput(this, 'DatabasePort', {
343
+ value: this.database.instanceEndpoint.port.toString(),
344
+ description: 'RDS Database Port',
345
+ exportName: `${this.stackName}-DatabasePort`,
346
+ });
347
+
348
+ new cdk.CfnOutput(this, 'EfsFileSystemId', {
349
+ value: this.fileSystem.fileSystemId,
350
+ description: 'EFS File System ID',
351
+ exportName: `${this.stackName}-EfsFileSystemId`,
352
+ });
353
+
354
+ // Security Group IDs for import
355
+ new cdk.CfnOutput(this, 'DbSecurityGroupId', {
356
+ value: this.dbSecurityGroup.securityGroupId,
357
+ description: 'Database Security Group ID',
358
+ exportName: `${this.stackName}-DbSecurityGroupId`,
359
+ });
360
+
361
+ new cdk.CfnOutput(this, 'EcsSecurityGroupId', {
362
+ value: this.ecsSecurityGroup.securityGroupId,
363
+ description: 'ECS Security Group ID',
364
+ exportName: `${this.stackName}-EcsSecurityGroupId`,
365
+ });
366
+
367
+ new cdk.CfnOutput(this, 'AlbSecurityGroupId', {
368
+ value: this.albSecurityGroup.securityGroupId,
369
+ description: 'ALB Security Group ID',
370
+ exportName: `${this.stackName}-AlbSecurityGroupId`,
371
+ });
372
+
373
+ // Secret ARNs for import
374
+ new cdk.CfnOutput(this, 'DbCredentialsSecretArn', {
375
+ value: this.dbCredentials.secretArn,
376
+ description: 'Database Credentials Secret ARN',
377
+ exportName: `${this.stackName}-DbCredentialsSecretArn`,
378
+ });
379
+
380
+ new cdk.CfnOutput(this, 'AppSecretsSecretArn', {
381
+ value: this.appSecrets.secretArn,
382
+ description: 'App Secrets Secret ARN',
383
+ exportName: `${this.stackName}-AppSecretsSecretArn`,
384
+ });
385
+
386
+ new cdk.CfnOutput(this, 'JwtSecretArn', {
387
+ value: this.jwtSecret.secretArn,
388
+ description: 'JWT Secret ARN',
389
+ exportName: `${this.stackName}-JwtSecretArn`,
390
+ });
391
+
392
+ new cdk.CfnOutput(this, 'GoogleOAuthSecretArn', {
393
+ value: this.googleOAuth.secretArn,
394
+ description: 'Google OAuth Secret ARN',
395
+ exportName: `${this.stackName}-GoogleOAuthSecretArn`,
396
+ });
397
+
398
+ new cdk.CfnOutput(this, 'GitHubOAuthSecretArn', {
399
+ value: this.githubOAuth.secretArn,
400
+ description: 'GitHub OAuth Secret ARN',
401
+ exportName: `${this.stackName}-GitHubOAuthSecretArn`,
402
+ });
403
+
404
+ new cdk.CfnOutput(this, 'AdminEmailsSecretArn', {
405
+ value: this.adminEmails.secretArn,
406
+ description: 'Admin Emails Secret ARN',
407
+ exportName: `${this.stackName}-AdminEmailsSecretArn`,
408
+ });
409
+
410
+ new cdk.CfnOutput(this, 'AdminPasswordSecretArn', {
411
+ value: this.adminPassword.secretArn,
412
+ description: 'Admin Password Secret ARN',
413
+ exportName: `${this.stackName}-AdminPasswordSecretArn`,
414
+ });
415
+ }
416
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "app": "npx ts-node --prefer-ts-exts cdk/app.ts",
3
+ "watch": {
4
+ "include": ["**"],
5
+ "exclude": [
6
+ "README.md",
7
+ "cdk*.json",
8
+ "**/*.js",
9
+ "**/*.d.ts",
10
+ "node_modules",
11
+ "test"
12
+ ]
13
+ },
14
+ "context": {
15
+ "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
16
+ "@aws-cdk/core:checkSecretUsage": true,
17
+ "@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
18
+ "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
19
+ "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
20
+ "@aws-cdk/aws-ecs:arnFormat": true,
21
+ "@aws-cdk/aws-iam:minimizePolicies": true,
22
+ "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
23
+ "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
24
+ "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
25
+ "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
26
+ "@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
27
+ "@aws-cdk/core:enablePartitionLiterals": true,
28
+ "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
29
+ "@aws-cdk/aws-iam:standardizedServicePrincipals": true,
30
+ "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
31
+ "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
32
+ "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
33
+ "@aws-cdk/aws-route53-patters:useCertificate": true,
34
+ "@aws-cdk/customresources:installLatestAwsSdkDefault": false
35
+ }
36
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "ci",
3
+ "site": {
4
+ "domain": "localhost",
5
+ "oauthAllowedDomains": ["example.com", "test.example.com"]
6
+ },
7
+ "platform": {
8
+ "default": "posix"
9
+ },
10
+ "env": {
11
+ "NODE_ENV": "development"
12
+ },
13
+ "services": {
14
+ "backend": {
15
+ "platform": {
16
+ "type": "posix"
17
+ },
18
+ "port": 4000,
19
+ "publicURL": "http://localhost:4000",
20
+ "corsOrigin": "http://localhost:3000"
21
+ },
22
+ "frontend": {
23
+ "platform": {
24
+ "type": "posix"
25
+ },
26
+ "port": 3000,
27
+ "url": "http://localhost:3000",
28
+ "siteName": "CI Test Site"
29
+ },
30
+ "filesystem": {
31
+ "platform": {
32
+ "type": "posix"
33
+ },
34
+ "path": "data",
35
+ "description": "CI filesystem storage"
36
+ },
37
+ "database": {
38
+ "platform": {
39
+ "type": "external"
40
+ },
41
+ "type": "postgres",
42
+ "host": "localhost",
43
+ "port": 5432
44
+ },
45
+ "mcp": {
46
+ "platform": {
47
+ "type": "posix"
48
+ },
49
+ "dependsOn": ["backend"]
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "local",
3
+ "platform": {
4
+ "default": "container"
5
+ },
6
+ "deployment": {
7
+ "imageTagStrategy": "mutable"
8
+ },
9
+ "site": {
10
+ "domain": "localhost:3000",
11
+ "oauthAllowedDomains": ["example.com", "localhost:3000"]
12
+ },
13
+ "env": {
14
+ "NODE_ENV": "development"
15
+ },
16
+ "services": {
17
+ "backend": {
18
+ "platform": {
19
+ "type": "posix"
20
+ },
21
+ "command": "npm run dev",
22
+ "port": 4000,
23
+ "publicURL": "http://localhost:4000",
24
+ "corsOrigin": "http://localhost:3000"
25
+ },
26
+ "frontend": {
27
+ "platform": {
28
+ "type": "posix"
29
+ },
30
+ "command": "npm run dev",
31
+ "port": 3000,
32
+ "url": "http://localhost:3000"
33
+ },
34
+ "database": {
35
+ "platform": {
36
+ "type": "container"
37
+ },
38
+ "image": "postgres:15-alpine",
39
+ "name": "semiont-local-db",
40
+ "port": 5432,
41
+ "environment": {
42
+ "POSTGRES_DB": "semiont",
43
+ "POSTGRES_USER": "postgres",
44
+ "POSTGRES_PASSWORD": "localpass"
45
+ }
46
+ },
47
+ "graph": {
48
+ "platform": {
49
+ "type": "external"
50
+ },
51
+ "type": "neo4j",
52
+ "name": "neo4j",
53
+ "uri": "${NEO4J_URI}",
54
+ "username": "${NEO4J_USERNAME}",
55
+ "password": "${NEO4J_PASSWORD}",
56
+ "database": "${NEO4J_DATABASE}"
57
+ },
58
+ "mcp": {
59
+ "platform": {
60
+ "type": "posix"
61
+ },
62
+ "dependsOn": ["backend"]
63
+ },
64
+ "filesystem": {
65
+ "platform": {
66
+ "type": "posix"
67
+ },
68
+ "path": "./data/uploads",
69
+ "description": "Local filesystem storage for uploads and assets"
70
+ },
71
+ "inference": {
72
+ "platform": {
73
+ "type": "external"
74
+ },
75
+ "type": "anthropic",
76
+ "model": "claude-sonnet-4-20250514",
77
+ "maxTokens": 8192,
78
+ "endpoint": "https://api.anthropic.com",
79
+ "apiKey": "${ANTHROPIC_API_KEY}"
80
+ }
81
+ }
82
+ }