Skip to content

feat: implement tenant provisioning handler#9

Merged
alanshaw merged 7 commits into
ash/feat/vaultfrom
ash/feat/implement-tenant-provisioning-handler
Jul 2, 2026
Merged

feat: implement tenant provisioning handler#9
alanshaw merged 7 commits into
ash/feat/vaultfrom
ash/feat/implement-tenant-provisioning-handler

Conversation

@alanshaw

Copy link
Copy Markdown
Member

Implements the tenant provisioning handler (PUT /tenants/{tenantId}).

  • Adds config for did:plc directory server
  • Creates and wires did:plc directory client
  • Added external ID to tenant DB table
  • Creates tenant private key (currently secp256k1)
    • Stores private key in vault
    • Registers private key with did:plc directory
  • Stores tenant details in DB

Depends on:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Implements tenant provisioning via PUT /tenants/{tenantId} by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

Changes:

  • Added external_id to tenant storage model and introduced GetByExternalID for idempotent provisioning.
  • Added PLC directory configuration + fx wiring to provide a plc.DirectoryClient.
  • Implemented ProvisionTenant API handler with accompanying tests (memory stores + httptest PLC server).

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/store/tenant/tenant.go Extends tenant record + store interface to include ExternalID and GetByExternalID.
pkg/store/tenant/tenant_test.go Updates tenant store tests for ExternalID and external-id lookups/uniqueness.
pkg/store/tenant/postgres/store.go Adds external_id persistence and retrieval in Postgres tenant store.
pkg/store/tenant/memory/store.go Adds ExternalID storage and GetByExternalID to memory tenant store.
pkg/migrations/sql/00001_init.sql Updates tenant schema to include external_id.
pkg/fx/plc.go New fx module/provider to build a did:plc directory client from config.
pkg/fx/plc_test.go Unit tests for PLC client construction/validation.
pkg/fx/config.go Wires PLCConfig into the fx-provided config bundle.
pkg/fx/app.go Adds PLCModule to the application module graph.
pkg/config/config.go Adds plc.directory config + defaults + flag binding.
pkg/api/tenants.go Implements PUT /tenants/:tenantId provisioning flow and tenant response mapping.
pkg/api/tenants_test.go Adds handler tests covering provisioning + idempotency + validation.
go.mod Bumps ucantone dependency and adds new indirect deps for secp256k1.
go.sum Updates checksums for the updated dependencies.
cmd/main.go Adds --plc-directory flag for configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/migrations/sql/00001_init.sql
Comment thread pkg/store/tenant/memory/store.go
Comment thread pkg/api/tenants.go
Comment thread pkg/api/tenants_test.go
@alanshaw
alanshaw requested review from Peeja, bajtos and frrist June 26, 2026 11:04

@Peeja Peeja left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM, modulo a minor logging concern.

Comment thread pkg/api/tenants.go Outdated
// Publish the genesis operation to register the did:plc.
if err := plcClient.Update(ctx, tenantID, genesis); err != nil {
log.Error("publishing genesis operation", zap.Error(err))
_ = secrets.Delete(ctx, vaultKey) // best-effort cleanup of the orphaned key

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should we at least log if there's an error?

Comment thread pkg/api/tenants.go Outdated
// Record the tenant.
if err := tenants.Add(ctx, tenantID, externalID, prov.ID, req.DisplayName, tenant.Active); err != nil {
if errors.Is(err, store.ErrRecordExists) {
_ = secrets.Delete(ctx, vaultKey) // best-effort cleanup of the orphaned key

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(Ditto.)

Comment thread pkg/api/tenants.go
if err := plcClient.Update(ctx, tenantID, genesis); err != nil {
log.Error("publishing genesis operation", zap.Error(err))
_ = secrets.Delete(ctx, vaultKey) // best-effort cleanup of the orphaned key
return echo.NewHTTPError(http.StatusBadGateway, "failed to register tenant DID")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

What's the recovery story for this failure point?

  1. In FilOne Console web app, I create my first bucket in the Forge region
  2. FilOne Console backend calls PUT /tenants/{orgId}
  3. Hilt checks the DB, the tenant is not there yet
  4. Hilt generates a new tenant identity and stores the signer in Vault
  5. Hilt fails to register the tenant with PLC and returns 502 Bad Gateway
  6. FilOne Console backends return a 500 internal error to the user

Since Console didn't provide any details to the user, they may retry the same operation.

  1. I try to create my first bucket in the Forge region again
  2. FilOne Console backend calls PUT /tenants/{orgId}
  3. Hilt checks the DB, the tenant is not there yet
  4. Hilt generates a new tenant identity and stores the signer in Vault
    • The Vault entry created in the first run is orphaned
    • Shouldn't we check the Vault first and use the existing signer if it already exists?
  5. Let's say Hilt succeeds in registering the tenant with PLC now
  6. Hilt stores the tenant info in the DB and all is good (except the orphaned Vault entry)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think it's important to have some unit tests to cover this scenario.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ideally secrets.Delete(...) will clean up the orphan. I will check the tests exercise failures and assert on the clean up.

Comment thread pkg/api/tenants.go
}

// Publish the genesis operation to register the did:plc.
if err := plcClient.Update(ctx, tenantID, genesis); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we add retry with exponential backoff to gracefully handle temporary service degradation or unavailability of PLC?

Also, is there any timeout/deadline enforced for this call to a 3rd-party API?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ideally I'd like to push retries all the way back to the client so we don't end up adding retry logic for every database/API call we make in these handlers.

I will configure the HTTP client with a timeout though - good call.

alanshaw and others added 2 commits July 2, 2026 11:37
Implements the "get", "update status" and "delete" handlers.

Adds additional access key and bucket store methods for listing items by
tenant as well as deleting.

⚠️ Note: cleanup of bucket data from the forge network is not handled
here.

---

Adds stubs for the UCAN RPC handlers needed per the RFC.

---

Implements the access key handlers of the management API.
@alanshaw
alanshaw merged commit efc53c3 into ash/feat/vault Jul 2, 2026
4 checks passed
alanshaw added a commit that referenced this pull request Jul 2, 2026
…10) (#11) (#12)

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
) (#11) (#12)

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
) (#12)

Adds initial Tenant + Access Key management API scaffolding and wires store backends (memory/postgres) into the Fx app based on configuration, including new config types/flags and Postgres migration hooks.

**Changes:**
- Introduces API route stubs and request/response types for tenant + access key endpoints, and auto-registers routes onto the Echo server via an Fx group.
- Adds storage backend selection (memory vs postgres) in `AppModule`, plus Fx modules to provide the corresponding store implementations.
- Expands configuration to include `storage.*` and `storage.postgres.*` settings and flag bindings; standardizes PG unique-violation handling using `pgerrcode`.

---

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
…) (#11) (#12)

Adds initial scaffolding for running hilt as a service: a Cobra-based `hilt serve` command that loads config via Viper and starts an Echo HTTP server wired together with Uber Fx.

**Changes:**
- Introduce Fx modules for config surfacing, zap logging, and Echo server lifecycle management.
- Add Viper-based config loading with defaults, env var support, and pflag binding.
- Add `cmd/main.go` CLI entrypoint plus basic Makefile/.gitignore and new dependencies (Echo, Cobra, Viper, Fx).

---

Adds initial Tenant + Access Key management API scaffolding and wires store backends (memory/postgres) into the Fx app based on configuration, including new config types/flags and Postgres migration hooks.

**Changes:**
- Introduces API route stubs and request/response types for tenant + access key endpoints, and auto-registers routes onto the Echo server via an Fx group.
- Adds storage backend selection (memory vs postgres) in `AppModule`, plus Fx modules to provide the corresponding store implementations.
- Expands configuration to include `storage.*` and `storage.postgres.*` settings and flag bindings; standardizes PG unique-violation handling using `pgerrcode`.

---

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
) (#12)

Adds a new Go client for interacting with the upload service via UCAN RPC invocations, along with structured logging helpers to make invocation arguments/metadata easier to inspect in logs.

**Changes:**
- Introduce `pkg/client.UploadClient` with `RegisterCustomer` (`/customer/add`) and `ProvisionSpace` (`/provider/add`) operations.
- Add `pkg/lib/zapucan` helpers for logging UCAN invocations and decoding CBOR-encoded IPLD maps into zap fields.
- Bump `github.com/fil-forge/libforge` dependency version.

---

Adds initial scaffolding for running hilt as a service: a Cobra-based `hilt serve` command that loads config via Viper and starts an Echo HTTP server wired together with Uber Fx.

**Changes:**
- Introduce Fx modules for config surfacing, zap logging, and Echo server lifecycle management.
- Add Viper-based config loading with defaults, env var support, and pflag binding.
- Add `cmd/main.go` CLI entrypoint plus basic Makefile/.gitignore and new dependencies (Echo, Cobra, Viper, Fx).

---

Adds initial Tenant + Access Key management API scaffolding and wires store backends (memory/postgres) into the Fx app based on configuration, including new config types/flags and Postgres migration hooks.

**Changes:**
- Introduces API route stubs and request/response types for tenant + access key endpoints, and auto-registers routes onto the Echo server via an Fx group.
- Adds storage backend selection (memory vs postgres) in `AppModule`, plus Fx modules to provide the corresponding store implementations.
- Expands configuration to include `storage.*` and `storage.postgres.*` settings and flag bindings; standardizes PG unique-violation handling using `pgerrcode`.

---

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
)

Adds a new Go client for interacting with the upload service via UCAN RPC invocations, along with structured logging helpers to make invocation arguments/metadata easier to inspect in logs.

**Changes:**
- Introduce `pkg/client.UploadClient` with `RegisterCustomer` (`/customer/add`) and `ProvisionSpace` (`/provider/add`) operations.
- Add `pkg/lib/zapucan` helpers for logging UCAN invocations and decoding CBOR-encoded IPLD maps into zap fields.
- Bump `github.com/fil-forge/libforge` dependency version.

---

Adds initial scaffolding for running hilt as a service: a Cobra-based `hilt serve` command that loads config via Viper and starts an Echo HTTP server wired together with Uber Fx.

**Changes:**
- Introduce Fx modules for config surfacing, zap logging, and Echo server lifecycle management.
- Add Viper-based config loading with defaults, env var support, and pflag binding.
- Add `cmd/main.go` CLI entrypoint plus basic Makefile/.gitignore and new dependencies (Echo, Cobra, Viper, Fx).

---

Adds initial Tenant + Access Key management API scaffolding and wires store backends (memory/postgres) into the Fx app based on configuration, including new config types/flags and Postgres migration hooks.

**Changes:**
- Introduces API route stubs and request/response types for tenant + access key endpoints, and auto-registers routes onto the Echo server via an Fx group.
- Adds storage backend selection (memory vs postgres) in `AppModule`, plus Fx modules to provide the corresponding store implementations.
- Expands configuration to include `storage.*` and `storage.postgres.*` settings and flag bindings; standardizes PG unique-violation handling using `pgerrcode`.

---

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
alanshaw added a commit that referenced this pull request Jul 2, 2026
) (#8) (#9) (#10) (#11) (#12)

This PR introduces a new `pkg/store` layer with both in-memory and Postgres-backed implementations for core entities (provider, tenant, bucket, access key, delegation), plus embedded goose migrations and test utilities to run Postgres-backed store tests via testcontainers.

**Changes:**
- Add store interfaces + in-memory and Postgres implementations for provider/tenant/bucket/accesskey/delegation.
- Add embedded goose migrations and a migrations runner to initialize Postgres schema.
- Add test utilities and cross-implementation tests (memory + Postgres) for each store.

---

Adds a new Go client for interacting with the upload service via UCAN RPC invocations, along with structured logging helpers to make invocation arguments/metadata easier to inspect in logs.

**Changes:**
- Introduce `pkg/client.UploadClient` with `RegisterCustomer` (`/customer/add`) and `ProvisionSpace` (`/provider/add`) operations.
- Add `pkg/lib/zapucan` helpers for logging UCAN invocations and decoding CBOR-encoded IPLD maps into zap fields.
- Bump `github.com/fil-forge/libforge` dependency version.

---

Adds initial scaffolding for running hilt as a service: a Cobra-based `hilt serve` command that loads config via Viper and starts an Echo HTTP server wired together with Uber Fx.

**Changes:**
- Introduce Fx modules for config surfacing, zap logging, and Echo server lifecycle management.
- Add Viper-based config loading with defaults, env var support, and pflag binding.
- Add `cmd/main.go` CLI entrypoint plus basic Makefile/.gitignore and new dependencies (Echo, Cobra, Viper, Fx).

---

Adds initial Tenant + Access Key management API scaffolding and wires store backends (memory/postgres) into the Fx app based on configuration, including new config types/flags and Postgres migration hooks.

**Changes:**
- Introduces API route stubs and request/response types for tenant + access key endpoints, and auto-registers routes onto the Echo server via an Fx group.
- Adds storage backend selection (memory vs postgres) in `AppModule`, plus Fx modules to provide the corresponding store implementations.
- Expands configuration to include `storage.*` and `storage.postgres.*` settings and flag bindings; standardizes PG unique-violation handling using `pgerrcode`.

---

Adds a configurable “partner key” authentication mechanism to gate access to the Tenant API routes, wiring the key through config/flags and enforcing it via Echo middleware.

**Changes:**
- Introduces `AuthConfig` (`auth.partner_key`) and exposes it through the fx config module.
- Wraps all Tenant API routes in partner-key bearer auth middleware while keeping `/` and `/health` open.
- Adds new Echo middleware + unit tests for partner-key bearer authentication.

---

Adds a KMS-agnostic `vault.Vault` interface plus in-memory and HashiCorp Vault (KV v2) implementations, and wires vault selection/config into the fx app and CLI so Hilt can persist private key material in a configurable backend.

**Changes:**
- Introduce `pkg/vault.Vault` interface with `ErrNotFound`, plus an in-memory implementation and a HashiCorp Vault (KV v2) implementation (including AppRole auth helper).
- Add config schema + viper defaults + CLI flags for selecting/configuring the vault backend, and wire backend selection into the fx app graph.
- Add tests for the vault interface contract and HashiCorp AppRole login using a testcontainers Vault dev container.

---

Implements tenant provisioning via `PUT /tenants/{tenantId}` by introducing an external tenant identifier, wiring a did:plc directory client, generating/storing tenant keys in Vault, and persisting tenant records.

**Changes:**
- Added `external_id` to tenant storage model and introduced `GetByExternalID` for idempotent provisioning.
- Added PLC directory configuration + fx wiring to provide a `plc.DirectoryClient`.
- Implemented `ProvisionTenant` API handler with accompanying tests (memory stores + httptest PLC server).

---

Implements the remaining tenant management endpoints by wiring real store-backed handlers and adding the store capabilities required to support tenant deletion cascades (buckets, access keys, delegations, vault secrets, and did:plc deactivation).

**Changes:**
- Implement `GET /tenants/:tenantId`, `POST /tenants/:tenantId/status`, and `DELETE /tenants/:tenantId` handlers (with did:plc deactivation + cascade cleanup).
- Extend tenant/bucket/access-key stores with `Delete` and “list by tenant” capabilities, including pagination for buckets.
- Add/extend unit tests for the new store methods and HTTP handlers; bump dependencies to newer `ucantone` and `dag-json-gen`.

---

This PR implements the Management API access-key endpoints, including key material storage in Vault and issuing tenant→access-key UCAN delegations based on requested S3 permissions. It also extends the store layer to support access-key expiry and efficient bucket lookups by ID for response rendering.

**Changes:**
- Implement Create/List/Get/Delete access-key handlers, including Vault secret storage and delegation issuance/revocation.
- Add `expires_at` support for access keys across schema, store implementations, and tests.
- Extend bucket listing to support filtering by explicit bucket IDs via new `bucket.ListOption`/`bucket.ListConfig`.

---

Adds initial scaffolding for Hilt’s UCAN-based RPC surface (used by Ingot for S3 tenant management), including an identity-backed UCAN server mounted on the existing Echo HTTP server.

**Changes:**
- Introduces UCAN RPC handler stubs and wires them into an `fx` module that builds a `ucantone` HTTP server.
- Adds a configurable service identity (PEM-backed or ephemeral) and exposes a public `did:web` DID document endpoint.
- Extends configuration/CLI flags to support identity settings.
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.

4 participants