6.6 KiB
Add @suaveplan/storage-cdn-rackspace — Rackspace Cloud Files + Rackspace CDN adapter
Package: @suaveplan/storage-cdn-rackspace
Status: PROPOSED
Type: new-package (genesis-original)
- Design: ./design.md
- Spec delta: ./specs/storage-cdn-rackspace/spec.md
Why
The user operates production traffic from Rackspace CDN (LON region) with Cloud Files as origin. storage-http covers generic prefix-based reads but knows nothing about Cloud Files auth, container CDN-enablement, purge, or TempURL signing. This package provides a first-class CdnAdapter so application code can write storage.set(key, bytes) / storage.get(key) / storage.purge(key) / storage.signUrl(key, { ttlSeconds }) against Rackspace without leaking provider knowledge into business logic.
Rackspace Cloud Files is OpenStack Swift; the API is well-documented and stable. The CDN side adds container CDN enablement, X-Cdn-Uri discovery, and a per-account TempURL HMAC key. This package implements both layers via injected FetchLike so it can be tested without live API access.
What changes
- ADDED:
RackspaceCdnAdapterclass extendingCdnAdapterfrom@suaveplan/storage-cdn. - ADDED:
createRackspaceCdnAdapter(options)factory. - ADDED:
RackspaceCdnAdapterOptionsinterface + Zod schema. Required:authUrl(defaulthttps://lon.identity.api.rackspacecloud.com/v2.0),username,apiKey,region(defaultLON),container. Optional:tempUrlKey,cdnEnabled(defaulttrue),fetch,tokenCache,headers,retry. - ADDED:
RackspaceAuthClient— handles v2 identity auth (POST /v2.0/tokens), token cache with expiry, automatic re-auth on401. ExposesgetStorageEndpoint()andgetCdnEndpoint()resolved from the service catalog for the configured region. - ADDED:
RackspaceCdnClient—enableCdn(container),getCdnMetadata(container)(parsesX-Cdn-Uri,X-Cdn-Ssl-Uri,X-Ttl,X-Cdn-Enabled),purgeObject(container, key)(DELETEon/v1/services/cdn/<container>/<key>withX-Purge-Emailhonoured if provided). - ADDED:
RackspaceTempUrl— HMAC-SHA1 TempURL signer per the Swift TempURL middleware spec: signed string<METHOD>\n<EXPIRES>\n<PATH>with the accounttemp-url-key(or container-scoped key). Optionaltemp-url-prefixmode. - ADDED:
STORAGE_CDN_RACKSPACE_CODES— 9 codes:AUTH_FAILED,TOKEN_EXPIRED,CONTAINER_NOT_FOUND,CDN_NOT_ENABLED,TEMP_URL_KEY_MISSING,PURGE_RATE_LIMITED,REGION_NOT_IN_CATALOG,INVALID_OPTIONS,UPSTREAM_ERROR.StorageCdnRackspaceErrorclass withcontainer/region/httpStatusdiscriminants. - ADDED: Capabilities —
partialReads: true(Swift supportsRange),nativeStreams: true,partialWrites: false,atomicWrites: false,contentAddressable: false;cdn: { cdnPurge: true, cdnPrefixPurge: false, cdnSignedUrls: true, cdnOriginUploads: true }. - ADDED: Module-augmentation registry —
declare module "@suaveplan/storage"addsrackspace-cdn: RackspaceCdnAdapterslot;registerRackspaceCdnProvider()factory hook (idempotent). - ADDED:
@suaveplan/retryintegration on transient (429,5xx, network), withRetry-Afterhonoured. - ADDED: OTel surface — same shape as
storage-http(storage_cdn_rackspace.op_duration_ms,…io_bytes,…reads_total,…writes_total,…purges_total,…errors_total), with extraprovider="rackspace"attribute.
Out of scope
- Non-LON regions. The package supports the full region catalog (LON, IAD, DFW, ORD, SYD, HKG) —
regionis just a config field — but the test fixture is LON-only because that is the user's deployment. Other regions verified by unit tests against a mocked service catalog response. - v1 auth. Rackspace v1 auth (
X-Auth-User/X-Auth-Keyagainstauth.api.rackspacecloud.com) is deprecated; this package targets v2 identity exclusively. - CDN log delivery. Cloud Files CDN log retrieval API is operations tooling; out of scope.
- Bulk operations. Swift
bulk-deleteandbulk-uploadare not exposed viaAbstractStorage. Callers loop. - Static Web hosting headers (
X-Container-Meta-Web-Index,…-Error). Belong in deployment tooling, not the adapter.
Source mapping
Genesis-original. References:
- OpenStack Swift API v1: https://docs.openstack.org/api-ref/object-store/
- Rackspace Cloud Files developer guide (TempURL, CDN enablement)
- Rackspace identity v2: https://docs.rackspace.com/docs/cloud-identity-v2
Requirements with no source line
| Requirement | Justification |
|---|---|
| Token cache with proactive refresh | Auth tokens expire (24h default). Without a cache, every request re-auths. Proactive refresh (5 min before expiry) avoids the cliff where in-flight requests start 401-ing. |
| Service-catalog lookup vs. hard-coded endpoints | Rackspace returns the canonical region endpoint in the auth response. Hard-coding LON URLs would break for IAD/DFW/etc. and silently misroute if Rackspace renumbers. |
| TempURL key stored in adapter options, not fetched per-call | Reading the container or account meta on every signUrl would be wasteful. Caller provides the key (rotated via Rackspace control panel) at construction. |
cdnPrefixPurge: false |
Rackspace CDN purge API is per-object. Prefix purge requires listing + per-key calls (rate-limited at ~25/min). Honest declaration. |
Verification approach
src/rackspace-cdn-adapter/rackspace-cdn-adapter.test.ts— CRUD via injectedFetchLike, range reads, signed-URL round-trip, purge happy path, READ_ONLY onlistif container too large.src/rackspace-cdn-adapter/rackspace-cdn-adapter.spec-conformance.test.ts—instanceof CdnAdapter+AbstractStorage, capabilities matrix, everySTORAGE_CDN_RACKSPACEcode reachable.src/auth-client/auth-client.test.ts— v2 auth round-trip, token cache hit/miss, proactive refresh underFakeClock, service-catalog lookup for LON / IAD.src/cdn-client/cdn-client.test.ts—enableCdn,getCdnMetadataparsing,purgeObjecthappy path, 429 retry withRetry-After.src/temp-url/temp-url.test.ts— HMAC-SHA1 signature byte-equal to Swift reference vectors,temp-url-prefixmode, expired-signature rejected.src/registry/registry.test.ts—createStorage({ type: "rackspace-cdn", config })returnsRackspaceCdnAdapter, idempotent re-registration.
Impact
Affected dependents
- No dependents yet (
bunx turbo ls --filter=...@suaveplan/storage-cdn-rackspace).
Affected upstreams
@suaveplan/storage-cdn—StorageProviderRegistrymodule-augmented withrackspace-cdnslot. Additive. [x] no action needed.- Hard dependency on
@suaveplan/storage-cdn(must be archived first).