@raindancers/raindancers-crew 0.0.0 → 0.0.1
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/.jsii +3 -3
- package/README.md +200 -0
- package/lib/crew-backup-bucket.js +1 -1
- package/lib/fargate-crew-base.js +1 -1
- package/lib/fargate-crew.js +1 -1
- package/lib/remote-crew-instance.js +1 -1
- package/package.json +1 -1
package/.jsii
CHANGED
|
@@ -9030,7 +9030,7 @@
|
|
|
9030
9030
|
},
|
|
9031
9031
|
"name": "@raindancers/raindancers-crew",
|
|
9032
9032
|
"readme": {
|
|
9033
|
-
"markdown": "# @raindancers/raindancers-crew\n\nA CDK construct that provisions a **self-hosted [KiroCrew](https://github.com/kirodotdev/KiroCrew) gateway** on a single EC2 instance in your own AWS account, reached over **SSM Session Manager** — no inbound ports, no SSH key.\n\nIt is a pipeline-native, version-controlled port of the upstream `kirocrew-ec2` CloudFormation template. Where the native `kirocrew cloud launch` is an imperative one-shot, this construct lets you deploy the same shape **through your own CDK pipeline**, under **your** naming, permissions boundary, and OIDC deploy role — so a remote crew becomes a reviewed, repeatable, diffable artifact like everything else you ship.\n\n## Getting started\n\n### 1. Install\n\n```bash\nnpm install @raindancers/raindancers-crew\n```\n\nThe package targets `aws-cdk-lib` ^2.260.0 and `constructs` ^10 (peer dependencies — your app supplies them).\n\n### 2. Define a stack\n\nA complete stack that stands up an EC2 crew with off-box backup. Everything is\ntyped and compiles as-is — fill in your account, region, VPC lookup, and\npermissions-boundary ARN.\n\n```ts\nimport { App, Stack, StackProps } from 'aws-cdk-lib';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { Construct } from 'constructs';\nimport {\n RemoteCrewInstance,\n CrewArchitecture,\n CrewBackupBucket,\n} from '@raindancers/raindancers-crew';\n\nclass CrewStack extends Stack {\n constructor(scope: Construct, id: string, props: StackProps) {\n super(scope, id, props);\n\n // Your existing VPC (or ec2.Vpc.fromLookup(...)).\n const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });\n\n // Durable, versioned, KMS-encrypted backup destination.\n const backup = new CrewBackupBucket(this, 'CrewBackup');\n\n new RemoteCrewInstance(this, 'Crew', {\n vpc,\n // REQUIRED: the instance runs a prompt-injectable agent, so its role\n // must be capped by a permissions boundary you pre-create.\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n instanceType: new ec2.InstanceType('m7g.2xlarge'),\n // Pin a released tag for reproducible deploys — the default `main` drifts.\n source: { kirocrewRef: 'v0.8.0' },\n // Off-box backup: grants scoped write + installs a daily snapshot timer.\n backupBucket: backup,\n backupSchedule: 'daily',\n });\n }\n}\n\nconst app = new App();\nnew CrewStack(app, 'RemoteCrew', {\n env: { account: '123456789012', region: 'eu-west-2' },\n});\napp.synth();\n```\n\n### 3. Deploy through your pipeline\n\nThis is a plain CDK stack — deploy it however you deploy everything else\n(a CDK Pipelines / agentl stage, or `cdk deploy` from a keyless-OIDC CI job).\nThe `WaitCondition` blocks stack completion until the gateway is actually\nserving, so a green deploy means a live crew (and a failed bootstrap rolls the\nstack back with the setup-log tail in the failure reason).\n\n```bash\ncdk deploy RemoteCrew\n```\n\n### 4. Connect\n\nAccess is **SSM-only** — no inbound ports. Open a port-forward to the\nloopback dashboard and browse `http://127.0.0.1:5476`:\n\n```bash\n# The construct exports the instance id under a stable name derived from the\n# stackTag (default 'kirocrew'): kirocrew-<stackTag>-instance-id\nINSTANCE_ID=$(aws cloudformation list-exports \\\n --query \"Exports[?Name=='kirocrew-kirocrew-instance-id'].Value\" --output text)\n\naws ssm start-session --target \"$INSTANCE_ID\" \\\n --document-name AWS-StartPortForwardingSession \\\n --parameters '{\"portNumber\":[\"5476\"],\"localPortNumber\":[\"5476\"]}'\n```\n\n(Or use the upstream `kirocrew cloud connect`, which mints the dashboard token\non the box and opens the tunnel for you.)\n\n### 5. Verify backup\n\nThe daily timer pushes a redaction-scrubbed snapshot to the backup bucket. Fire\none immediately and confirm it landed:\n\n```bash\n# On the instance (via SSM Session Manager):\nsudo systemctl start kirocrew-backup.service\nsudo journalctl -u kirocrew-backup.service --no-pager | tail\n\n# From your workstation — the stable key a replacement instance restores from:\naws s3 ls \"s3://<backup-bucket>/crew-snapshots/latest.tar\"\n```\n\nTo rebuild a **replacement** instance from backup, deploy a fresh `CrewStack`\npointing at the same bucket, then on the new box run\n`sudo kirocrew-restore-from-s3` (pulls `latest.tar`, stops the gateway,\nrestores in replace mode, restarts).\n\n> **Container crew instead of an instance?** See [Fargate lane](#fargate-lane)\n> below — swap `RemoteCrewInstance` for `FargateCrewBase` + `FargateCrew`.\n\nThe sections below are the per-construct reference.\n\n## What it creates\n\n| Resource | Notes |\n|---|---|\n| IAM role + instance profile | `AmazonSSMManagedInstanceCore` + a **required permissions boundary**; scoped `s3:GetObject` only when an S3 source is used |\n| Security group | **No inbound** by default (SSM-only); optional `tcp/22` from `allowSshCidr` (≤ /16); optional webhook port from a single source SG via `webhookIngress`; IPv6 egress when `enableIpv6` |\n| EC2 instance | Amazon Linux 2023, arch-aware AMI, **IMDSv2 enforced**, **encrypted gp3** root |\n| WaitConditionHandle + WaitCondition | Blocks stack completion until the gateway is actually serving on the loopback dashboard port |\n\n## Security posture (preserved from upstream)\n\n- **SSM-only access.** No inbound rules unless you explicitly pass `allowSshCidr`. Access is via SSM port-forward; the public DNS is diagnostics only.\n- **IMDSv2 required, hop limit 1.** KiroCrew runs a prompt-injectable agent that executes arbitrary tools — an SSRF/injection reaching the metadata endpoint must not be able to read the role's STS credentials via IMDSv1.\n- **Permissions boundary is mandatory** on the instance role.\n- **Node.js tarball is SHA-256-pinned** and verified before root extraction.\n- **Encrypted EBS root.**\n\n## Private dual-stack brain (55minutes posture)\n\nFor an always-on brain that egresses over IPv6 with **no public IPv4**, is woken\nby the native KiroCrew webhook, and runs as an autopilot crew that never\nidle-closes, compose the additive props below. Everything here is optional and\ndefaults off — omit it all and you get the SSM-only public-subnet box above.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { RemoteCrewInstance, CrewArchitecture } from '@raindancers/raindancers-crew';\n\n// vpc: a dual-stack VPC whose private subnets carry IPv6 CIDRs and route\n// ::/0 to an Egress-Only Internet Gateway (see \"What stays yours\" below).\n// ingestLambdaSg: the SG of the consumer's ingest Lambda / reverse proxy —\n// imported, this construct never creates it.\nnew RemoteCrewInstance(this, 'Brain', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n source: { kirocrewRef: 'v0.8.0' },\n\n // Private + dual-stack: IPv6 egress, no public IPv4.\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },\n associatePublicIp: false,\n enableIpv6: true,\n\n // Webhook reach from ONE source SG only (never a CIDR), authenticated by a\n // Bearer token fetched from Secrets Manager at boot (written to config.json\n // as hooks.webhook_token; the instance role gets GetSecretValue on this ARN).\n webhookIngress: { source: ingestLambdaSg },\n webhookTokenSecretArn:\n 'arn:aws:secretsmanager:eu-west-2:123456789012:secret:kc/webhook-token-AbCdEf',\n\n // Always-on: autopilot + never idle-close.\n crewRuntime: { autopilot: true, disableIdleClose: true },\n});\n```\n\n### What stays yours (this construct provisions none of it)\n\n`RemoteCrewInstance` consumes an `IVpc` and provisions no routing, so these live\nin your VPC / app, not the library:\n\n- **Egress-Only Internet Gateway** and the `::/0` IPv6 egress route — the VPC's\n concern. `enableIpv6` assigns the instance an IPv6 address and opens IPv6\n egress on its SG, but the route to the internet is yours.\n- **fck-nat (or managed NAT)** for any IPv4 egress you still need.\n- **The ingest Lambda and its security group** — you build it and pass its SG as\n `webhookIngress.source`. The construct only accepts it.\n- **Routable exposure of the webhook.** The KiroCrew gateway binds `127.0.0.1`\n only (it has no routable listener), so `webhookIngress` opens the SG but a\n **consumer-owned reverse proxy or SSH tunnel on the box** is what actually\n forwards `POST /api/hooks/agent` from the source SG to the loopback gateway.\n The dashboard stays loopback/SSM-only regardless.\n\n## Connecting\n\n`RemoteCrewInstance` provisions the box; **connection stays an operator action** (it is inherently imperative — mint a short-lived token on the instance and open an SSM port-forward). Use the upstream `kirocrew cloud connect` against the deployed instance id, or your own SSM `start-session` / port-forward wrapper.\n\n## Fargate lane\n\nFor a container-based crew there are two sibling constructs, mirroring the upstream `kirocrew-fargate-base` + `kirocrew-fargate-crew` split:\n\n- **`FargateCrewBase`** — one per account/region: the ECS cluster crew tasks run on plus an egress-only (no-inbound) task security group. Deleting a crew must not delete the shared cluster, so this is separate.\n- **`FargateCrew`** — one per crew: the execution role (secret read scoped to `kirocrew/crew/<crew>/*`, log write, optional private-ECR pull), the **zero-policy task role** (the running container's blast radius — it must never gain `secretsmanager:GetSecretValue`), and the crew's log group. All names are derived from the crew name.\n\n```ts\nimport { FargateCrewBase, FargateCrew } from '@raindancers/raindancers-crew';\n\nconst base = new FargateCrewBase(this, 'CrewBase', { vpc });\nnew FargateCrew(this, 'Crew', {\n crew: 'fiftyfive',\n // permissionsBoundaryArn: '...', // optional until the shared boundary creator lands\n // ecrRepositoryArn: '...', // only for a private image; public registry needs no grant\n});\n```\n\nSecurity invariants preserved from upstream: task role has no policies and never reads secrets; execution-role secret read is crew-scoped with individually-listed actions (no prefix wildcards); both assume-role trusts carry an `aws:SourceAccount` condition. The permissions boundary is **optional** here (a declared degraded mode) because no creator for the crew boundary exists yet — unlike the EC2 lane, where it is mandatory.\n\n**EC2 vs Fargate:** EC2 gives a persistent box with local disk (the crew's memory/knowledge DBs live on the instance) and is the native launcher's default; Fargate is more ephemeral and expects external persistence. For a remote crew that remembers across sessions, EC2 is usually the better fit.\n\n## Backing up the crew's learnings\n\nA remote crew's value is its accumulated memory, lessons, and knowledge — which on the EC2 lane live on the instance's local disk. KiroCrew's built-in backup (`kirocrew snapshot`) produces a redaction-scrubbed bundle (the signing key, `.env`, and execution logs never ship), but writes it **locally** — so it survives corruption, not instance loss. These constructs add the missing **off-box durability**.\n\n`CrewBackupBucket` provisions a hardened destination: SSE-KMS (rotating key), all public access blocked, TLS-only, **versioned**, with a lifecycle rule expiring stale noncurrent versions. Bucket and key `RETAIN` on stack delete, so the backups outlive a teardown.\n\n```ts\nimport { CrewBackupBucket, RemoteCrewInstance } from '@raindancers/raindancers-crew';\n\nconst backup = new CrewBackupBucket(this, 'CrewBackup');\n\nnew RemoteCrewInstance(this, 'Crew', {\n vpc,\n permissionsBoundaryArn: '...',\n backupBucket: backup, // grants scoped write + installs a daily timer\n backupSchedule: 'daily', // systemd OnCalendar\n});\n```\n\nOn the **EC2 lane** this grants the instance role scoped write, installs a **systemd timer** that runs `kirocrew snapshot --purpose backup` and uploads the newest bundle to S3 (a timestamped key for history plus a stable `latest.tar`), and installs a `kirocrew-restore-from-s3` helper. On the **Fargate lane**, pass the same bucket to `FargateCrew` — it grants the **task role** (the running container) write, since the container runs its own snapshot push.\n\n### Restore (rebuilding a replacement crew)\n\n`kirocrew restore <bundle> --mode replace|merge`:\n\n- **replace** clears the target's memory/knowledge trees and rebuilds them from the bundle (the knowledge DB and memory stores are replaced wholesale, not row-merged). Restore validates database integrity and refuses a truncated bundle; `sel_hmac.key` is regenerated (not restored). Use on a fresh replacement instance.\n- **merge** layers the bundle onto existing state without clearing. Use to seed a crew you want to keep.\n\nOn an EC2 instance provisioned with a backup bucket, `sudo kirocrew-restore-from-s3` pulls `latest.tar`, stops the gateway, restores in **replace** mode, and restarts — a one-command rebuild.\n\n## License\n\nApache-2.0\n"
|
|
9033
|
+
"markdown": "# @raindancers/raindancers-crew\n\nA CDK construct that provisions a **self-hosted [KiroCrew](https://github.com/kirodotdev/KiroCrew) gateway** on a single EC2 instance in your own AWS account, reached over **SSM Session Manager** — no inbound ports, no SSH key.\n\nIt is a pipeline-native, version-controlled port of the upstream `kirocrew-ec2` CloudFormation template. Where the native `kirocrew cloud launch` is an imperative one-shot, this construct lets you deploy the same shape **through your own CDK pipeline**, under **your** naming, permissions boundary, and OIDC deploy role — so a remote crew becomes a reviewed, repeatable, diffable artifact like everything else you ship.\n\n## Architecture\n\nWhat `RemoteCrewInstance` creates (solid) and what your VPC/app owns (dashed). The default is the SSM-only public-subnet box; the IPv6 egress and webhook ingress are opt-in props.\n\n```mermaid\nflowchart TB\n subgraph vpc[\"Your VPC (you own routing)\"]\n subgraph subnet[\"Subnet (public, or private-with-egress)\"]\n instance[\"EC2 instance<br/>Amazon Linux 2023, IMDSv2<br/>encrypted gp3 root<br/>kirocrew gateway on 127.0.0.1:5476\"]\n sg[\"Security group<br/>no inbound by default<br/>IPv6 egress if enableIpv6<br/>webhook port if webhookIngress\"]\n instance --- sg\n end\n eigw([\"Egress-Only IGW<br/>(consumer-owned, IPv6)\"])\n nat([\"NAT / fck-nat<br/>(consumer-owned, IPv4)\"])\n proxy([\"Reverse proxy<br/>(consumer-owned, fronts loopback webhook)\"])\n end\n\n role[\"IAM role<br/>SSM core + required permissions boundary<br/>scoped S3 GetObject / secret GetSecretValue\"]\n wait[\"WaitCondition<br/>blocks stack until gateway is healthy\"]\n operator([\"Operator\"])\n secret[(\"Secrets Manager<br/>webhook Bearer token\")]\n ingest([\"Ingest Lambda SG<br/>(consumer-owned)\"])\n\n instance --- role\n instance -.-> wait\n operator -- \"SSM port-forward (no inbound)\" --> instance\n instance -. \"IPv6 egress\" .-> eigw\n instance -. \"IPv4 egress\" .-> nat\n ingest -. \"webhook, source-SG only\" .-> proxy -.-> instance\n instance -. \"fetch token at boot\" .-> secret\n\n classDef owned fill:#e8f0fe,stroke:#4285f4;\n classDef consumer fill:#f5f5f5,stroke:#999,stroke-dasharray:4 3;\n class instance,sg,role,wait owned;\n class eigw,nat,proxy,ingest,secret consumer;\n```\n\n## Getting started\n\n### 1. Install\n\n```bash\nnpm install @raindancers/raindancers-crew\n```\n\nThe package targets `aws-cdk-lib` ^2.260.0 and `constructs` ^10 (peer dependencies — your app supplies them).\n\n### 2. Define a stack\n\nA complete stack that stands up an EC2 crew with off-box backup. Everything is\ntyped and compiles as-is — fill in your account, region, VPC lookup, and\npermissions-boundary ARN.\n\n```ts\nimport { App, Stack, StackProps } from 'aws-cdk-lib';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { Construct } from 'constructs';\nimport {\n RemoteCrewInstance,\n CrewArchitecture,\n CrewBackupBucket,\n} from '@raindancers/raindancers-crew';\n\nclass CrewStack extends Stack {\n constructor(scope: Construct, id: string, props: StackProps) {\n super(scope, id, props);\n\n // Your existing VPC (or ec2.Vpc.fromLookup(...)).\n const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });\n\n // Durable, versioned, KMS-encrypted backup destination.\n const backup = new CrewBackupBucket(this, 'CrewBackup');\n\n new RemoteCrewInstance(this, 'Crew', {\n vpc,\n // REQUIRED: the instance runs a prompt-injectable agent, so its role\n // must be capped by a permissions boundary you pre-create.\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n instanceType: new ec2.InstanceType('m7g.2xlarge'),\n // Pin a released tag for reproducible deploys — the default `main` drifts.\n source: { kirocrewRef: 'v0.8.0' },\n // Off-box backup: grants scoped write + installs a daily snapshot timer.\n backupBucket: backup,\n backupSchedule: 'daily',\n });\n }\n}\n\nconst app = new App();\nnew CrewStack(app, 'RemoteCrew', {\n env: { account: '123456789012', region: 'eu-west-2' },\n});\napp.synth();\n```\n\n### 3. Deploy through your pipeline\n\nThis is a plain CDK stack — deploy it however you deploy everything else\n(a CDK Pipelines / agentl stage, or `cdk deploy` from a keyless-OIDC CI job).\nThe `WaitCondition` blocks stack completion until the gateway is actually\nserving, so a green deploy means a live crew (and a failed bootstrap rolls the\nstack back with the setup-log tail in the failure reason).\n\n```bash\ncdk deploy RemoteCrew\n```\n\n### 4. Connect\n\nAccess is **SSM-only** — no inbound ports. Open a port-forward to the\nloopback dashboard and browse `http://127.0.0.1:5476`:\n\n```bash\n# The construct exports the instance id under a stable name derived from the\n# stackTag (default 'kirocrew'): kirocrew-<stackTag>-instance-id\nINSTANCE_ID=$(aws cloudformation list-exports \\\n --query \"Exports[?Name=='kirocrew-kirocrew-instance-id'].Value\" --output text)\n\naws ssm start-session --target \"$INSTANCE_ID\" \\\n --document-name AWS-StartPortForwardingSession \\\n --parameters '{\"portNumber\":[\"5476\"],\"localPortNumber\":[\"5476\"]}'\n```\n\n(Or use the upstream `kirocrew cloud connect`, which mints the dashboard token\non the box and opens the tunnel for you.)\n\n### 5. Verify backup\n\nThe daily timer pushes a redaction-scrubbed snapshot to the backup bucket. Fire\none immediately and confirm it landed:\n\n```bash\n# On the instance (via SSM Session Manager):\nsudo systemctl start kirocrew-backup.service\nsudo journalctl -u kirocrew-backup.service --no-pager | tail\n\n# From your workstation — the stable key a replacement instance restores from:\naws s3 ls \"s3://<backup-bucket>/crew-snapshots/latest.tar\"\n```\n\nTo rebuild a **replacement** instance from backup, deploy a fresh `CrewStack`\npointing at the same bucket, then on the new box run\n`sudo kirocrew-restore-from-s3` (pulls `latest.tar`, stops the gateway,\nrestores in replace mode, restarts).\n\n> **Container crew instead of an instance?** See [Fargate lane](#fargate-lane)\n> below — swap `RemoteCrewInstance` for `FargateCrewBase` + `FargateCrew`.\n\nThe sections below are the per-construct reference.\n\n## What it creates\n\n| Resource | Notes |\n|---|---|\n| IAM role + instance profile | `AmazonSSMManagedInstanceCore` + a **required permissions boundary**; scoped `s3:GetObject` only when an S3 source is used |\n| Security group | **No inbound** by default (SSM-only); optional `tcp/22` from `allowSshCidr` (≤ /16); optional webhook port from a single source SG via `webhookIngress`; IPv6 egress when `enableIpv6` |\n| EC2 instance | Amazon Linux 2023, arch-aware AMI, **IMDSv2 enforced**, **encrypted gp3** root |\n| WaitConditionHandle + WaitCondition | Blocks stack completion until the gateway is actually serving on the loopback dashboard port |\n\n## Security posture (preserved from upstream)\n\n- **SSM-only access.** No inbound rules unless you explicitly pass `allowSshCidr`. Access is via SSM port-forward; the public DNS is diagnostics only.\n- **IMDSv2 required, hop limit 1.** KiroCrew runs a prompt-injectable agent that executes arbitrary tools — an SSRF/injection reaching the metadata endpoint must not be able to read the role's STS credentials via IMDSv1.\n- **Permissions boundary is mandatory** on the instance role.\n- **Node.js tarball is SHA-256-pinned** and verified before root extraction.\n- **Encrypted EBS root.**\n\n## Private dual-stack brain (55minutes posture)\n\nFor an always-on brain that egresses over IPv6 with **no public IPv4**, is woken\nby the native KiroCrew webhook, and runs as an autopilot crew that never\nidle-closes, compose the additive props below. Everything here is optional and\ndefaults off — omit it all and you get the SSM-only public-subnet box above.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { RemoteCrewInstance, CrewArchitecture } from '@raindancers/raindancers-crew';\n\n// vpc: a dual-stack VPC whose private subnets carry IPv6 CIDRs and route\n// ::/0 to an Egress-Only Internet Gateway (see \"What stays yours\" below).\n// ingestLambdaSg: the SG of the consumer's ingest Lambda / reverse proxy —\n// imported, this construct never creates it.\nnew RemoteCrewInstance(this, 'Brain', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n source: { kirocrewRef: 'v0.8.0' },\n\n // Private + dual-stack: IPv6 egress, no public IPv4.\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },\n associatePublicIp: false,\n enableIpv6: true,\n\n // Webhook reach from ONE source SG only (never a CIDR), authenticated by a\n // Bearer token fetched from Secrets Manager at boot (written to config.json\n // as hooks.webhook_token; the instance role gets GetSecretValue on this ARN).\n webhookIngress: { source: ingestLambdaSg },\n webhookTokenSecretArn:\n 'arn:aws:secretsmanager:eu-west-2:123456789012:secret:kc/webhook-token-AbCdEf',\n\n // Always-on: autopilot + never idle-close.\n crewRuntime: { autopilot: true, disableIdleClose: true },\n});\n```\n\n### What stays yours (this construct provisions none of it)\n\n`RemoteCrewInstance` consumes an `IVpc` and provisions no routing, so these live\nin your VPC / app, not the library:\n\n- **Egress-Only Internet Gateway** and the `::/0` IPv6 egress route — the VPC's\n concern. `enableIpv6` assigns the instance an IPv6 address and opens IPv6\n egress on its SG, but the route to the internet is yours.\n- **fck-nat (or managed NAT)** for any IPv4 egress you still need.\n- **The ingest Lambda and its security group** — you build it and pass its SG as\n `webhookIngress.source`. The construct only accepts it.\n- **Routable exposure of the webhook.** The KiroCrew gateway binds `127.0.0.1`\n only (it has no routable listener), so `webhookIngress` opens the SG but a\n **consumer-owned reverse proxy or SSH tunnel on the box** is what actually\n forwards `POST /api/hooks/agent` from the source SG to the loopback gateway.\n The dashboard stays loopback/SSM-only regardless.\n\n## Examples\n\nWorked implementations for the common shapes. Each is a complete construct\ninstantiation against the real props — fill in your account, region, VPC, and\npermissions-boundary ARN.\n\n### 1. Minimal SSM-only crew (the simplest thing that works)\n\nA single crew box in a public subnet, reached only over SSM. No backup, no\nwebhook, defaults everywhere.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { RemoteCrewInstance } from '@raindancers/raindancers-crew';\n\nconst vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });\n\nnew RemoteCrewInstance(this, 'Crew', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n // Pin a released tag for reproducible deploys — the default `main` drifts.\n source: { kirocrewRef: 'v0.8.0' },\n});\n```\n\n### 2. Crew with off-box backup\n\nAdd a hardened, versioned, KMS-encrypted backup bucket; the construct grants the\ninstance role write + read and installs a daily snapshot timer.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport {\n RemoteCrewInstance,\n CrewBackupBucket,\n CrewArchitecture,\n} from '@raindancers/raindancers-crew';\n\nconst vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });\nconst backup = new CrewBackupBucket(this, 'CrewBackup');\n\nnew RemoteCrewInstance(this, 'Crew', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n instanceType: new ec2.InstanceType('m7g.2xlarge'),\n source: { kirocrewRef: 'v0.8.0' },\n backupBucket: backup,\n backupSchedule: 'daily', // systemd OnCalendar expression\n backupPrefix: 'crew-snapshots/',\n});\n```\n\n### 3. Source from an S3 tarball instead of a git clone\n\nFor air-gapped or pinned-artifact deploys: the instance role is granted\n`s3:GetObject` on exactly that one object, nothing wider.\n\n```ts\nnew RemoteCrewInstance(this, 'Crew', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n source: {\n sourceBucket: 'my-artifacts-bucket',\n sourceKey: 'kirocrew/kirocrew-src-v0.8.0.tar.gz',\n },\n});\n```\n\n### 4. Private dual-stack always-on brain (the full 55minutes posture)\n\nPrivate subnet, IPv6 egress, no public IPv4, webhook reachable from one source\nSG, authenticated by a Secrets Manager token, running as an autopilot crew that\nnever idle-closes. See [Private dual-stack brain](#private-dual-stack-brain-55minutes-posture)\nabove for the prop-by-prop walkthrough.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { RemoteCrewInstance, CrewArchitecture } from '@raindancers/raindancers-crew';\n\n// ingestLambdaSg: the SG of your ingest Lambda / reverse proxy — imported.\nnew RemoteCrewInstance(this, 'Brain', {\n vpc,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',\n architecture: CrewArchitecture.ARM64,\n source: { kirocrewRef: 'v0.8.0' },\n\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },\n associatePublicIp: false,\n enableIpv6: true,\n\n webhookIngress: { source: ingestLambdaSg },\n webhookTokenSecretArn:\n 'arn:aws:secretsmanager:eu-west-2:123456789012:secret:kc/webhook-token-AbCdEf',\n\n crewRuntime: { autopilot: true, disableIdleClose: true },\n});\n```\n\n### 5. Fargate lane — shared base + one crew\n\nFor a container-based crew: `FargateCrewBase` once per account/region (the ECS\ncluster + egress-only SG), then one `FargateCrew` per crew (its two roles + log\ngroup). Deleting a crew never touches the shared cluster.\n\n```ts\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport {\n FargateCrewBase,\n FargateCrew,\n FargateCpuArchitecture,\n CrewBackupBucket,\n} from '@raindancers/raindancers-crew';\n\nconst vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });\nconst backup = new CrewBackupBucket(this, 'CrewBackup');\n\n// One per account/region — the shared cluster + egress-only task SG.\nconst base = new FargateCrewBase(this, 'CrewBase', {\n vpc,\n cpuArchitecture: FargateCpuArchitecture.ARM64,\n});\n\n// One per crew — roles + log group. Task role gets the backup write grant\n// (the running container pushes snapshots), NEVER a secret-read grant.\nconst crew = new FargateCrew(this, 'ResearchCrew', {\n crew: 'research',\n logRetentionDays: 30,\n permissionsBoundaryArn:\n 'arn:aws:iam::123456789012:policy/kirocrew-crew-boundary',\n backupBucket: backup,\n});\n\n// base.cluster, base.securityGroup, crew.executionRole, crew.taskRole,\n// crew.logGroup, crew.secretNamePrefix are exposed for your RunTask launch spec.\n```\n\n### Access + webhook flow\n\nHow the crew is reached — SSM for the operator, the source-SG + reverse proxy\nfor the native webhook. Nothing dials the box directly.\n\n```mermaid\nsequenceDiagram\n actor Op as Operator\n participant SSM as SSM Session Manager\n participant GW as Gateway (127.0.0.1:5476)\n Op->>SSM: aws ssm start-session (port-forward)\n SSM->>GW: tunnel to loopback dashboard\n GW-->>Op: dashboard on localhost\n\n participant Src as Ingest Lambda (source SG)\n participant RP as Reverse proxy (consumer)\n participant Hook as Gateway /api/hooks/agent (loopback)\n Src->>RP: POST webhook (SG allows source only)\n RP->>Hook: forward with Bearer token\n Hook-->>RP: 202 accepted (agent turn queued)\n```\n\n## Connecting\n\n`RemoteCrewInstance` provisions the box; **connection stays an operator action** (it is inherently imperative — mint a short-lived token on the instance and open an SSM port-forward). Use the upstream `kirocrew cloud connect` against the deployed instance id, or your own SSM `start-session` / port-forward wrapper.\n\n## Fargate lane\n\nFor a container-based crew there are two sibling constructs, mirroring the upstream `kirocrew-fargate-base` + `kirocrew-fargate-crew` split:\n\n- **`FargateCrewBase`** — one per account/region: the ECS cluster crew tasks run on plus an egress-only (no-inbound) task security group. Deleting a crew must not delete the shared cluster, so this is separate.\n- **`FargateCrew`** — one per crew: the execution role (secret read scoped to `kirocrew/crew/<crew>/*`, log write, optional private-ECR pull), the **zero-policy task role** (the running container's blast radius — it must never gain `secretsmanager:GetSecretValue`), and the crew's log group. All names are derived from the crew name.\n\n```ts\nimport { FargateCrewBase, FargateCrew } from '@raindancers/raindancers-crew';\n\nconst base = new FargateCrewBase(this, 'CrewBase', { vpc });\nnew FargateCrew(this, 'Crew', {\n crew: 'fiftyfive',\n // permissionsBoundaryArn: '...', // optional until the shared boundary creator lands\n // ecrRepositoryArn: '...', // only for a private image; public registry needs no grant\n});\n```\n\nSecurity invariants preserved from upstream: task role has no policies and never reads secrets; execution-role secret read is crew-scoped with individually-listed actions (no prefix wildcards); both assume-role trusts carry an `aws:SourceAccount` condition. The permissions boundary is **optional** here (a declared degraded mode) because no creator for the crew boundary exists yet — unlike the EC2 lane, where it is mandatory.\n\n**EC2 vs Fargate:** EC2 gives a persistent box with local disk (the crew's memory/knowledge DBs live on the instance) and is the native launcher's default; Fargate is more ephemeral and expects external persistence. For a remote crew that remembers across sessions, EC2 is usually the better fit.\n\n## Backing up the crew's learnings\n\nA remote crew's value is its accumulated memory, lessons, and knowledge — which on the EC2 lane live on the instance's local disk. KiroCrew's built-in backup (`kirocrew snapshot`) produces a redaction-scrubbed bundle (the signing key, `.env`, and execution logs never ship), but writes it **locally** — so it survives corruption, not instance loss. These constructs add the missing **off-box durability**.\n\n`CrewBackupBucket` provisions a hardened destination: SSE-KMS (rotating key), all public access blocked, TLS-only, **versioned**, with a lifecycle rule expiring stale noncurrent versions. Bucket and key `RETAIN` on stack delete, so the backups outlive a teardown.\n\n```ts\nimport { CrewBackupBucket, RemoteCrewInstance } from '@raindancers/raindancers-crew';\n\nconst backup = new CrewBackupBucket(this, 'CrewBackup');\n\nnew RemoteCrewInstance(this, 'Crew', {\n vpc,\n permissionsBoundaryArn: '...',\n backupBucket: backup, // grants scoped write + installs a daily timer\n backupSchedule: 'daily', // systemd OnCalendar\n});\n```\n\nOn the **EC2 lane** this grants the instance role scoped write, installs a **systemd timer** that runs `kirocrew snapshot --purpose backup` and uploads the newest bundle to S3 (a timestamped key for history plus a stable `latest.tar`), and installs a `kirocrew-restore-from-s3` helper. On the **Fargate lane**, pass the same bucket to `FargateCrew` — it grants the **task role** (the running container) write, since the container runs its own snapshot push.\n\n### Restore (rebuilding a replacement crew)\n\n`kirocrew restore <bundle> --mode replace|merge`:\n\n- **replace** clears the target's memory/knowledge trees and rebuilds them from the bundle (the knowledge DB and memory stores are replaced wholesale, not row-merged). Restore validates database integrity and refuses a truncated bundle; `sel_hmac.key` is regenerated (not restored). Use on a fresh replacement instance.\n- **merge** layers the bundle onto existing state without clearing. Use to seed a crew you want to keep.\n\nOn an EC2 instance provisioned with a backup bucket, `sudo kirocrew-restore-from-s3` pulls `latest.tar`, stops the gateway, restores in **replace** mode, and restarts — a one-command rebuild.\n\n## License\n\nApache-2.0\n"
|
|
9034
9034
|
},
|
|
9035
9035
|
"repository": {
|
|
9036
9036
|
"type": "git",
|
|
@@ -10547,6 +10547,6 @@
|
|
|
10547
10547
|
"symbolId": "src/remote-crew-instance-props:WebhookIngress"
|
|
10548
10548
|
}
|
|
10549
10549
|
},
|
|
10550
|
-
"version": "0.0.
|
|
10551
|
-
"fingerprint": "
|
|
10550
|
+
"version": "0.0.1",
|
|
10551
|
+
"fingerprint": "A5AvdunRFYN8r4XBntPYX4JGlwA5YOKB6CXSPSrf9ls="
|
|
10552
10552
|
}
|
package/README.md
CHANGED
|
@@ -4,6 +4,43 @@ A CDK construct that provisions a **self-hosted [KiroCrew](https://github.com/ki
|
|
|
4
4
|
|
|
5
5
|
It is a pipeline-native, version-controlled port of the upstream `kirocrew-ec2` CloudFormation template. Where the native `kirocrew cloud launch` is an imperative one-shot, this construct lets you deploy the same shape **through your own CDK pipeline**, under **your** naming, permissions boundary, and OIDC deploy role — so a remote crew becomes a reviewed, repeatable, diffable artifact like everything else you ship.
|
|
6
6
|
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
What `RemoteCrewInstance` creates (solid) and what your VPC/app owns (dashed). The default is the SSM-only public-subnet box; the IPv6 egress and webhook ingress are opt-in props.
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
flowchart TB
|
|
13
|
+
subgraph vpc["Your VPC (you own routing)"]
|
|
14
|
+
subgraph subnet["Subnet (public, or private-with-egress)"]
|
|
15
|
+
instance["EC2 instance<br/>Amazon Linux 2023, IMDSv2<br/>encrypted gp3 root<br/>kirocrew gateway on 127.0.0.1:5476"]
|
|
16
|
+
sg["Security group<br/>no inbound by default<br/>IPv6 egress if enableIpv6<br/>webhook port if webhookIngress"]
|
|
17
|
+
instance --- sg
|
|
18
|
+
end
|
|
19
|
+
eigw(["Egress-Only IGW<br/>(consumer-owned, IPv6)"])
|
|
20
|
+
nat(["NAT / fck-nat<br/>(consumer-owned, IPv4)"])
|
|
21
|
+
proxy(["Reverse proxy<br/>(consumer-owned, fronts loopback webhook)"])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
role["IAM role<br/>SSM core + required permissions boundary<br/>scoped S3 GetObject / secret GetSecretValue"]
|
|
25
|
+
wait["WaitCondition<br/>blocks stack until gateway is healthy"]
|
|
26
|
+
operator(["Operator"])
|
|
27
|
+
secret[("Secrets Manager<br/>webhook Bearer token")]
|
|
28
|
+
ingest(["Ingest Lambda SG<br/>(consumer-owned)"])
|
|
29
|
+
|
|
30
|
+
instance --- role
|
|
31
|
+
instance -.-> wait
|
|
32
|
+
operator -- "SSM port-forward (no inbound)" --> instance
|
|
33
|
+
instance -. "IPv6 egress" .-> eigw
|
|
34
|
+
instance -. "IPv4 egress" .-> nat
|
|
35
|
+
ingest -. "webhook, source-SG only" .-> proxy -.-> instance
|
|
36
|
+
instance -. "fetch token at boot" .-> secret
|
|
37
|
+
|
|
38
|
+
classDef owned fill:#e8f0fe,stroke:#4285f4;
|
|
39
|
+
classDef consumer fill:#f5f5f5,stroke:#999,stroke-dasharray:4 3;
|
|
40
|
+
class instance,sg,role,wait owned;
|
|
41
|
+
class eigw,nat,proxy,ingest,secret consumer;
|
|
42
|
+
```
|
|
43
|
+
|
|
7
44
|
## Getting started
|
|
8
45
|
|
|
9
46
|
### 1. Install
|
|
@@ -192,6 +229,169 @@ in your VPC / app, not the library:
|
|
|
192
229
|
forwards `POST /api/hooks/agent` from the source SG to the loopback gateway.
|
|
193
230
|
The dashboard stays loopback/SSM-only regardless.
|
|
194
231
|
|
|
232
|
+
## Examples
|
|
233
|
+
|
|
234
|
+
Worked implementations for the common shapes. Each is a complete construct
|
|
235
|
+
instantiation against the real props — fill in your account, region, VPC, and
|
|
236
|
+
permissions-boundary ARN.
|
|
237
|
+
|
|
238
|
+
### 1. Minimal SSM-only crew (the simplest thing that works)
|
|
239
|
+
|
|
240
|
+
A single crew box in a public subnet, reached only over SSM. No backup, no
|
|
241
|
+
webhook, defaults everywhere.
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
245
|
+
import { RemoteCrewInstance } from '@raindancers/raindancers-crew';
|
|
246
|
+
|
|
247
|
+
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });
|
|
248
|
+
|
|
249
|
+
new RemoteCrewInstance(this, 'Crew', {
|
|
250
|
+
vpc,
|
|
251
|
+
permissionsBoundaryArn:
|
|
252
|
+
'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',
|
|
253
|
+
// Pin a released tag for reproducible deploys — the default `main` drifts.
|
|
254
|
+
source: { kirocrewRef: 'v0.8.0' },
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### 2. Crew with off-box backup
|
|
259
|
+
|
|
260
|
+
Add a hardened, versioned, KMS-encrypted backup bucket; the construct grants the
|
|
261
|
+
instance role write + read and installs a daily snapshot timer.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
265
|
+
import {
|
|
266
|
+
RemoteCrewInstance,
|
|
267
|
+
CrewBackupBucket,
|
|
268
|
+
CrewArchitecture,
|
|
269
|
+
} from '@raindancers/raindancers-crew';
|
|
270
|
+
|
|
271
|
+
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });
|
|
272
|
+
const backup = new CrewBackupBucket(this, 'CrewBackup');
|
|
273
|
+
|
|
274
|
+
new RemoteCrewInstance(this, 'Crew', {
|
|
275
|
+
vpc,
|
|
276
|
+
permissionsBoundaryArn:
|
|
277
|
+
'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',
|
|
278
|
+
architecture: CrewArchitecture.ARM64,
|
|
279
|
+
instanceType: new ec2.InstanceType('m7g.2xlarge'),
|
|
280
|
+
source: { kirocrewRef: 'v0.8.0' },
|
|
281
|
+
backupBucket: backup,
|
|
282
|
+
backupSchedule: 'daily', // systemd OnCalendar expression
|
|
283
|
+
backupPrefix: 'crew-snapshots/',
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### 3. Source from an S3 tarball instead of a git clone
|
|
288
|
+
|
|
289
|
+
For air-gapped or pinned-artifact deploys: the instance role is granted
|
|
290
|
+
`s3:GetObject` on exactly that one object, nothing wider.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
new RemoteCrewInstance(this, 'Crew', {
|
|
294
|
+
vpc,
|
|
295
|
+
permissionsBoundaryArn:
|
|
296
|
+
'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',
|
|
297
|
+
source: {
|
|
298
|
+
sourceBucket: 'my-artifacts-bucket',
|
|
299
|
+
sourceKey: 'kirocrew/kirocrew-src-v0.8.0.tar.gz',
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 4. Private dual-stack always-on brain (the full 55minutes posture)
|
|
305
|
+
|
|
306
|
+
Private subnet, IPv6 egress, no public IPv4, webhook reachable from one source
|
|
307
|
+
SG, authenticated by a Secrets Manager token, running as an autopilot crew that
|
|
308
|
+
never idle-closes. See [Private dual-stack brain](#private-dual-stack-brain-55minutes-posture)
|
|
309
|
+
above for the prop-by-prop walkthrough.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
313
|
+
import { RemoteCrewInstance, CrewArchitecture } from '@raindancers/raindancers-crew';
|
|
314
|
+
|
|
315
|
+
// ingestLambdaSg: the SG of your ingest Lambda / reverse proxy — imported.
|
|
316
|
+
new RemoteCrewInstance(this, 'Brain', {
|
|
317
|
+
vpc,
|
|
318
|
+
permissionsBoundaryArn:
|
|
319
|
+
'arn:aws:iam::123456789012:policy/kirocrew-ec2-boundary',
|
|
320
|
+
architecture: CrewArchitecture.ARM64,
|
|
321
|
+
source: { kirocrewRef: 'v0.8.0' },
|
|
322
|
+
|
|
323
|
+
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
|
|
324
|
+
associatePublicIp: false,
|
|
325
|
+
enableIpv6: true,
|
|
326
|
+
|
|
327
|
+
webhookIngress: { source: ingestLambdaSg },
|
|
328
|
+
webhookTokenSecretArn:
|
|
329
|
+
'arn:aws:secretsmanager:eu-west-2:123456789012:secret:kc/webhook-token-AbCdEf',
|
|
330
|
+
|
|
331
|
+
crewRuntime: { autopilot: true, disableIdleClose: true },
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 5. Fargate lane — shared base + one crew
|
|
336
|
+
|
|
337
|
+
For a container-based crew: `FargateCrewBase` once per account/region (the ECS
|
|
338
|
+
cluster + egress-only SG), then one `FargateCrew` per crew (its two roles + log
|
|
339
|
+
group). Deleting a crew never touches the shared cluster.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
343
|
+
import {
|
|
344
|
+
FargateCrewBase,
|
|
345
|
+
FargateCrew,
|
|
346
|
+
FargateCpuArchitecture,
|
|
347
|
+
CrewBackupBucket,
|
|
348
|
+
} from '@raindancers/raindancers-crew';
|
|
349
|
+
|
|
350
|
+
const vpc = ec2.Vpc.fromLookup(this, 'Vpc', { isDefault: true });
|
|
351
|
+
const backup = new CrewBackupBucket(this, 'CrewBackup');
|
|
352
|
+
|
|
353
|
+
// One per account/region — the shared cluster + egress-only task SG.
|
|
354
|
+
const base = new FargateCrewBase(this, 'CrewBase', {
|
|
355
|
+
vpc,
|
|
356
|
+
cpuArchitecture: FargateCpuArchitecture.ARM64,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// One per crew — roles + log group. Task role gets the backup write grant
|
|
360
|
+
// (the running container pushes snapshots), NEVER a secret-read grant.
|
|
361
|
+
const crew = new FargateCrew(this, 'ResearchCrew', {
|
|
362
|
+
crew: 'research',
|
|
363
|
+
logRetentionDays: 30,
|
|
364
|
+
permissionsBoundaryArn:
|
|
365
|
+
'arn:aws:iam::123456789012:policy/kirocrew-crew-boundary',
|
|
366
|
+
backupBucket: backup,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// base.cluster, base.securityGroup, crew.executionRole, crew.taskRole,
|
|
370
|
+
// crew.logGroup, crew.secretNamePrefix are exposed for your RunTask launch spec.
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Access + webhook flow
|
|
374
|
+
|
|
375
|
+
How the crew is reached — SSM for the operator, the source-SG + reverse proxy
|
|
376
|
+
for the native webhook. Nothing dials the box directly.
|
|
377
|
+
|
|
378
|
+
```mermaid
|
|
379
|
+
sequenceDiagram
|
|
380
|
+
actor Op as Operator
|
|
381
|
+
participant SSM as SSM Session Manager
|
|
382
|
+
participant GW as Gateway (127.0.0.1:5476)
|
|
383
|
+
Op->>SSM: aws ssm start-session (port-forward)
|
|
384
|
+
SSM->>GW: tunnel to loopback dashboard
|
|
385
|
+
GW-->>Op: dashboard on localhost
|
|
386
|
+
|
|
387
|
+
participant Src as Ingest Lambda (source SG)
|
|
388
|
+
participant RP as Reverse proxy (consumer)
|
|
389
|
+
participant Hook as Gateway /api/hooks/agent (loopback)
|
|
390
|
+
Src->>RP: POST webhook (SG allows source only)
|
|
391
|
+
RP->>Hook: forward with Bearer token
|
|
392
|
+
Hook-->>RP: 202 accepted (agent turn queued)
|
|
393
|
+
```
|
|
394
|
+
|
|
195
395
|
## Connecting
|
|
196
396
|
|
|
197
397
|
`RemoteCrewInstance` provisions the box; **connection stays an operator action** (it is inherently imperative — mint a short-lived token on the instance and open an SSM port-forward). Use the upstream `kirocrew cloud connect` against the deployed instance id, or your own SSM `start-session` / port-forward wrapper.
|
|
@@ -53,7 +53,7 @@ const constructs_1 = require("constructs");
|
|
|
53
53
|
* {@link grantRead} to let a replacement instance pull them for restore.
|
|
54
54
|
*/
|
|
55
55
|
class CrewBackupBucket extends constructs_1.Construct {
|
|
56
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.CrewBackupBucket", version: "0.0.
|
|
56
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.CrewBackupBucket", version: "0.0.1" };
|
|
57
57
|
/** The backup bucket. */
|
|
58
58
|
bucket;
|
|
59
59
|
/** The KMS key encrypting the bucket. */
|
package/lib/fargate-crew-base.js
CHANGED
|
@@ -60,7 +60,7 @@ var FargateCpuArchitecture;
|
|
|
60
60
|
* Port of the upstream `kirocrew-fargate-base` CloudFormation template.
|
|
61
61
|
*/
|
|
62
62
|
class FargateCrewBase extends constructs_1.Construct {
|
|
63
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.FargateCrewBase", version: "0.0.
|
|
63
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.FargateCrewBase", version: "0.0.1" };
|
|
64
64
|
/** The ECS cluster crew tasks run on. */
|
|
65
65
|
cluster;
|
|
66
66
|
/** The egress-only task security group (no inbound). */
|
package/lib/fargate-crew.js
CHANGED
|
@@ -68,7 +68,7 @@ const ALLOWED_RETENTION_DAYS = [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365];
|
|
|
68
68
|
* roles are not assumable on behalf of an unrelated stack's task.
|
|
69
69
|
*/
|
|
70
70
|
class FargateCrew extends constructs_1.Construct {
|
|
71
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.FargateCrew", version: "0.0.
|
|
71
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.FargateCrew", version: "0.0.1" };
|
|
72
72
|
/** Role ECS assumes BEFORE the container starts (secret fetch + log stream). */
|
|
73
73
|
executionRole;
|
|
74
74
|
/** Identity the RUNNING container carries. Created with no policies. */
|
|
@@ -68,7 +68,7 @@ const DEFAULT_TIMEOUT_MIN = 25;
|
|
|
68
68
|
* diagnostics.
|
|
69
69
|
*/
|
|
70
70
|
class RemoteCrewInstance extends constructs_1.Construct {
|
|
71
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.RemoteCrewInstance", version: "0.0.
|
|
71
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "@raindancers/raindancers-crew.RemoteCrewInstance", version: "0.0.1" };
|
|
72
72
|
/** The EC2 instance (SSM target). */
|
|
73
73
|
instance;
|
|
74
74
|
/** The instance's IAM role (carries the permissions boundary). */
|