Skip to content

feat: add S3-compatible endpoint support for custom object stores#895

Merged
stack72 merged 1 commit intomainfrom
s3-endpoint-support
Mar 27, 2026
Merged

feat: add S3-compatible endpoint support for custom object stores#895
stack72 merged 1 commit intomainfrom
s3-endpoint-support

Conversation

@adamhjk
Copy link
Copy Markdown
Contributor

@adamhjk adamhjk commented Mar 27, 2026

Summary

  • Thread optional endpoint and forcePathStyle fields through the S3 datastore stack so users can target S3-compatible providers like DigitalOcean Spaces
  • Both fields are conditionally spread (not passed as undefined) into the AWS SDK client constructor, preserving existing behavior when omitted
  • Adds --endpoint and --force-path-style CLI flags to swamp datastore setup s3
  • Displays endpoint in swamp datastore status output

Test Plan

  • Added 2 new tests for S3 config resolution with/without endpoint
  • Updated 4 existing status tests for the new endpoint field
  • Full test suite passes (3676/3676)
  • deno check, deno lint, deno fmt, deno run compile all pass

🤖 Generated with Claude Code

Thread optional `endpoint` and `forcePathStyle` fields through the S3
datastore stack so users can target S3-compatible providers like
DigitalOcean Spaces. Both fields are conditionally spread into the AWS
SDK client constructor, preserving existing behavior when omitted.

Adds `--endpoint` and `--force-path-style` CLI flags to
`swamp datastore setup s3` and supports the fields in `.swamp.yaml`.
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Clean, well-structured PR that threads endpoint and forcePathStyle through the full S3 stack consistently. No blocking issues found.

Blocking Issues

None.

Suggestions

  1. S3DatastoreVerifier constructor — consider a config object: The constructor now takes 5 positional optional parameters (bucket, prefix?, region?, endpoint?, forcePathStyle?), which is fragile — callers must remember the order and can't skip middle params. Consider accepting an S3ClientConfig (or a subset) instead, matching how S3Client itself is constructed. This is a pre-existing pattern issue that this PR extends, so not blocking.

  2. DatastoreSetupDeps interface positional params: The verifyS3Datastore, checkS3DatastoreExists, and pushToS3 function signatures in the deps interface similarly grow with positional optionals. Passing an options object would make these more maintainable and testable. Again, pre-existing pattern — not blocking for this PR.

Overall: DDD layering is correct (infrastructure config in infrastructure, domain types in domain), the libswamp import boundary is respected, tests cover the new fields, and the conditional spread pattern in S3Client constructor correctly avoids passing undefined to the AWS SDK.

LGTM ✅

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial Review

Critical / High

None.

Medium

  1. src/libswamp/datastores/setup.ts:311-318 — "already exists" YAML hint omits endpoint and forcePathStyle.
    When an S3 location already has a datastore, the error message prints a YAML snippet for the user to copy into .swamp.yaml. That snippet includes bucket, prefix, and region, but not endpoint or forcePathStyle. A user targeting DigitalOcean Spaces who hits this path would get an incomplete config suggestion that points at AWS S3 instead of their custom endpoint.

    Breaking example: swamp datastore setup s3 --bucket my-space --endpoint https://nyc3.digitaloceanspaces.com --force-path-style on a location that already has data → the YAML hint tells the user to configure without endpoint/forcePathStyle → they paste it → swamp datastore sync --pull fails because it tries to reach AWS.

    Suggested fix: Add the two fields to the YAML hint:

    (input.endpoint ? `    endpoint: ${input.endpoint}\n` : "") +
    (input.forcePathStyle ? `    forcePathStyle: true\n` : "") +

Low

  1. src/infrastructure/persistence/s3_datastore_verifier.ts:36-41 — Five positional optional parameters.
    The constructor signature (bucket, prefix?, region?, endpoint?, forcePathStyle?) is fragile — a caller could easily swap argument order (e.g., pass endpoint as region). An options object would be safer, matching the pattern used by S3Client. Not blocking since all current call sites are correct, but worth noting for future maintainability.

Verdict

PASS — Straightforward feature addition with correct conditional-spread logic, proper null handling (!= null for forcePathStyle), and no behavioral regressions. The medium finding is a UX papercut in an uncommon error path, not a correctness issue.

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLI UX Review

Blocking

None.

Suggestions

  1. Column alignment in src/presentation/renderers/datastore_status.ts: All other labels are padded to 9 characters before the value (Region: , Exclude: , Health: ), but the new entry uses Endpoint: (10 chars), shifting the value one column to the right. Consider Endpoint: with two trailing spaces to match the pattern, or accept this as a minor cosmetic inconsistency.

  2. forcePathStyle not surfaced in status output: --force-path-style is accepted at setup time and persisted to the marker, but it is not included in DatastoreStatusData, so swamp datastore status (both log and JSON mode) gives no indication whether path-style addressing is active. Users targeting S3-compatible stores may want to verify their config is correct. Worth adding forcePathStyle?: boolean to DatastoreStatusData and rendering it alongside Endpoint.

Verdict

PASS — new --endpoint and --force-path-style flags are well-named, clearly described (with a helpful example URL), and endpoint is correctly surfaced in both log and JSON status output. No blocking UX issues.

@stack72 stack72 merged commit d322581 into main Mar 27, 2026
10 checks passed
@stack72 stack72 deleted the s3-endpoint-support branch March 27, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants