Skip to content

feat(agent): port allocation for compose expose + ESM import fixes#24

Open
ivanbenko wants to merge 8 commits intomasterfrom
feat/agent-port-allocation-and-esm-fixes
Open

feat(agent): port allocation for compose expose + ESM import fixes#24
ivanbenko wants to merge 8 commits intomasterfrom
feat/agent-port-allocation-and-esm-fixes

Conversation

@ivanbenko
Copy link
Copy Markdown
Member

Summary

  • Agent port allocation: Compose role now manages host port allocation from a configured port_range. Services declare container ports via expose: in their compose file, and the agent allocates host ports bound to the client's ingress_bind_address. Port mappings are returned in the deploy response for ingress wiring.
  • Tmpfs merge: Hardening now merges client-specified tmpfs entries with global defaults instead of overwriting them, allowing containers like nginx to declare additional writable paths.
  • ESM forward compatibility: Added explicit .js extensions to 223 relative imports across all packages. Updated CLAUDE.md with ESM module resolution guidelines.
  • vault-fact-store fix: Corrected main field from dist/index.js to src/index.ts for workspace resolution.

Test plan

  • Deploy a compose project with expose: ["80"] and verify port is allocated from configured range
  • Verify port mappings returned in deploy response (ports field)
  • Verify tmpfs merge: client tmpfs entries preserved alongside global defaults
  • Verify pnpm build passes clean
  • Verify port release on project destroy
  • Canary publish to npm

🤖 Generated with Claude Code

ivanbenko and others added 2 commits April 3, 2026 11:56
…pfs mounts

The compose role now manages host port allocation from a configured range
instead of letting clients specify host port bindings directly. Services
declare container ports via `expose:` in their compose file, and the agent
allocates host ports from `port_range`, binding to the client's
`ingress_bind_address`. Port mappings are returned in the deploy response
so callers can wire ingress routes.

Also fixes tmpfs handling to merge client-specified entries with global
defaults instead of overwriting them, allowing containers like nginx to
declare additional tmpfs paths (e.g. /var/cache/nginx) alongside the
hardening defaults.

Updates CLAUDE.md with ESM module resolution guidelines and fixes
vault-fact-store main field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ivanbenko ivanbenko force-pushed the feat/agent-port-allocation-and-esm-fixes branch from 57ea12d to fd05649 Compare April 3, 2026 09:58
21 Go tests covering:
- Port allocator: allocation, reuse, reallocation on change, client
  isolation, range exhaustion, release, persistence across restarts
- Compose hardening: client ports stripped, allocated ports injected
  with bind address, expose cleared, tmpfs merge with defaults
- Helpers: parsePortRange validation, extractExposeEntries, portsMatch,
  parseTmpfsEntries type handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ivanbenko ivanbenko added canary and removed canary labels Apr 3, 2026
Replace individual remote commands for writing client policy files with
MirrorState from @opsen/docker-compose. This ensures stale policy files
are cleaned up when clients are removed, instead of only being deleted
when the Pulumi resource is destroyed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ivanbenko ivanbenko force-pushed the feat/agent-port-allocation-and-esm-fixes branch from 0533983 to 1f75f30 Compare April 3, 2026 10:38
@ivanbenko ivanbenko added canary and removed canary labels Apr 3, 2026
ivanbenko and others added 4 commits April 3, 2026 13:27
When a client policy changes (e.g. IngressBindAddress, hardening settings),
the reconciler detects stale deployments by comparing a policy hash stored
at deploy time with the current policy hash. Stale projects are automatically
re-hardened and redeployed.

Anti-loop guarantee: after successful redeploy, the stored hash is updated
to match the current policy, so the reconciler won't trigger again until
the policy actually changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All remote commands in AgentInstaller are now wrapped with sudo when the
SSH connection user is not root. Binary upload goes to /tmp first (SFTP
doesn't support sudo) then sudo mv to /usr/local/bin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lity

The agent package must keep type: module because Pulumi's ts-node CJS
loader cannot resolve .js extensions to .ts source files without it.
Since agent doesn't use Pulumi deep imports, ESM mode is safe here.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pulumi's ts-node always runs CJS regardless of type: module. Switch agent
to module: commonjs / moduleResolution: node in tsconfig, remove type:
module from package.json, strip .js extensions from relative imports, and
use __dirname instead of import.meta.dirname. This ensures the agent works
when consumed via file: references from Pulumi programs.

Verified with full e2e deployment on Hetzner Cloud via test.local.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ivanbenko ivanbenko added canary and removed canary labels Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant