ServerExtensions
Artifact Service
Management of extension UI bundles and private server-side asset delivery.
The ArtifactService manages the lifecycle of frontend assets that make up an extension. Assets are stored in private GCS and served through the CRM server proxy.
Bundle Structure
An extension bundle is a directory containing:
| File | Purpose |
|---|---|
index.html | Entry point |
manifest.json | Metadata (name, version, permissions) |
static/ | JS, CSS, images |
Storage Model
Artifacts are stored in private GCS with object key structure:
{orgId}/{extensionSlug}/{version}/{filePath}Examples
| Object Key | Description |
|---|---|
org_123/dashboard/1.0.0/index.html | Main HTML |
org_123/dashboard/1.0.0/static/bundle.js | JavaScript |
org_123/dashboard/1.0.0/static/styles.css | CSS |
Core API
deployExtracted
Uploads a bundle to private GCS and returns the entrypoint path:
async deployExtracted(
orgId: string,
extensionSlug: string,
version: string,
files: Map<string, Buffer>
): Promise<string> {
// Returns: /cdn/extensions/{slug}/{version}/index.html
}getArtifactUrl
Generates the URL for a specific artifact:
const url = await artifactService.getArtifactUrl({
orgSlug: "acme-corp",
extensionSlug: "dashboard",
version: "1.0.0",
filePath: "index.html",
});Asset Serving
The server exposes /cdn/extensions/* as an authenticated proxy:
// app.ts
app.get("/cdn/extensions/:slug/:version/*", async (c) => {
const { slug, version, path } = c.req.param();
// Verify installation active
// Stream from GCS
});Request Flow
Security
- Access Control: Proxy serves only active installations
- Path Traversal: Paths normalized and validated
- Tenant Isolation: Each org sees only their assets
- Signed URLs: Optional for time-limited access
Environment Variables
| Variable | Description |
|---|---|
EXTENSION_GCS_BUCKET | Private bucket name |
EXTENSION_GCS_PROJECT_ID | GCP project ID |
EXTENSION_GCS_KEY_FILE | Service account key path |
GCS_EMULATOR_HOST | Local emulator endpoint |
Troubleshooting
404 on Assets
- Verify object exists in GCS bucket
- Check installation is active for org
- Confirm version exists
403 on Assets
- Verify GCS permissions
- Check service account has bucket access
Proxy Failures
- Confirm object key format:
{orgId}/{slug}/{version}/{path} - Verify server can reach GCS