Lewati ke konten utama
Versi: v0.1.8

Project Bundles & Promotion

A project bundle is an immutable, numbered snapshot of a whole project — the unit Honeyframe uses to template one master project across many, and to promote a project across installs (for example test → prod). Where Governance and the promote-to-live lifecycle gate a single asset, bundles version and move entire projects.

Bundles build on the project export/import primitives, so a bundle is the same artifact those produce: a .hsproj.zip file containing the project's definition. Each bundle is stored out of the repo under the org's data directory as v{n}.hsproj.zip and is never mutated — creating a new snapshot always allocates the next version number.

All mutating operations require the org.admin permission and an explicit same-org check on the project. Listing operations require project.view.

Bundles shipped in v0.0.98. The UI lives in the Bundles section of a project's settings page; the same operations are exposed by the Python SDK.

What's in a bundle

A bundle captures the project's exported definition — recipes, dashboards, datasets, the flow, and project configuration. Heavier or environment-specific content is opt-in at create time via include-flags, all of which default to off:

FlagIncludes
include_versionsAsset version history
include_managed_folder_filesFiles stored in managed folders
include_vector_storesVector store contents
include_job_runsJob/run history
include_form_submissionsForm submission records
include_dbt_targetThe compiled dbt target

Connectors are not bundled. Connections are matched against the target org's catalog when a bundle is activated, deployed, or pushed; any that can't be mapped come back as {missing_connectors} (an HTTP 400), exactly as on a plain import. This keeps credentials out of the artifact and lets the same bundle land on installs with differently-named connections.

The create response reports the new version, the artifact size_bytes, a counts summary of what was captured, and required_connectors.

Creating & versioning

Snapshot the current state of a project as the next version:

curl -X POST https://platform.your-domain.com/api/projects/42/bundles \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "label": "release candidate", "include_managed_folder_files": true }'

The new version is always max(version) + 1 for that project, so versions are monotonic and a bundle is never overwritten. List them newest-first:

curl https://platform.your-domain.com/api/projects/42/bundles \
-H "Authorization: Bearer $TOKEN"

Each row carries version, label, the counts summary, size_bytes, and who created it. Download the stored zip for any version:

curl -OJ https://platform.your-domain.com/api/projects/42/bundles/3/download \
-H "Authorization: Bearer $TOKEN"
# -> bundle_p42_v3.hsproj.zip

If the artifact is missing on disk, download (and activate/deploy/push) returns HTTP 410.

Activate (clone)

Activate clones a bundle version into a brand-new target project. This is the templating path — snapshot a master project once, then stamp it out as many independent copies. You supply the new project's target_name and target_slug (lowercase alphanumeric with underscores/hyphens):

curl -X POST https://platform.your-domain.com/api/projects/42/bundles/3/activate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "target_name": "Regional Copy", "target_slug": "regional_copy" }'

Activation creates a fresh project, surfaces any {missing_connectors}, and records an activation in the audit trail — flipping any prior activations of that target inactive. By default the clone lands in the same org as the source; superadmins can pass target_org_id to clone cross-org.

Blue-green deploy & rollback

Deploy replaces an existing project's contents with a bundle version — the path for updating an already-deployed project, or rolling one back to an earlier version. From the operator's point of view the update happens in place: the target keeps its slug (the stable handle) and name.

Underneath, it is a safe blue-green swap, not a destructive in-place re-key. The bundle is imported as a fresh project under a throwaway slug, the prior project is soft-archived (recoverable via project restore), and only then is the stable slug + name handed to the freshly-imported project. Because import, archive, and slug-handoff share a single transaction, a connector or import failure leaves the existing project completely untouched.

curl -X POST https://platform.your-domain.com/api/projects/42/bundles/3/deploy \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "target_project_id": 57 }'

The response reports the deployed_project_id, the archived_project_id, the retained slug, and the counts/warnings from the import. The project's numeric id changes on every deploy; its slug does not — address deployed projects by slug, not id.

Guards reject deploying over the default project, over the bundle's own source project, or across orgs. Rollback uses this same endpoint against an earlier version — deploy v2 to move forward, deploy v1 again to roll back.

Cross-install push to a remote node

A bundle can also be promoted to a separate Honeyframe install — the test-install → prod-install promote boundary. First register the remote install as a node. A node holds a name, the remote base URL, and a personal access token (PAT) issued on the remote install. The PAT is encrypted at rest with AES-GCM (the same helper that protects LLM keys) and is never returned by the API — listing nodes omits it entirely.

# Register the remote install (org.admin)
curl -X POST https://platform.your-domain.com/api/nodes \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "prod", "base_url": "https://prod.your-domain.com", "api_key": "hf_…" }'

# List registered nodes (PAT never included)
curl https://platform.your-domain.com/api/nodes \
-H "Authorization: Bearer $TOKEN"

Then push a bundle version to a node. Honeyframe streams the stored zip to the remote install's import endpoint over HTTP (multipart, Bearer-authenticated with the decrypted PAT). The push is additive — it imports a new project on the remote and performs no destructive operations there:

curl -X POST https://platform.your-domain.com/api/projects/42/bundles/3/push \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "node_id": 1, "target_name": "Analytics", "target_slug": "analytics" }'

The remote matches connectors against its own catalog, so a remote {missing_connectors} comes back as a 400 just like a local activate; other remote failures surface as a 502. On success the response carries the remote_project_id, and the local activation history records the push with the node and the remote project id.

In-place content re-keying on the remote is intentionally unsupported — FK ordering, data-directory file moves, and path rewrites make it unsafe. Blue-green deploy is the safe equivalent; run it on the destination install after the push, or push subsequent versions as new projects.

From the SDK

The Python SDK (honeyframeapi) exposes the same lifecycle through a Bundle handle:

import honeyframeapi

client = honeyframeapi.Client("https://platform.your-domain.com", api_key="hf_…")
project = client.project(42)

# Create & list versions
b = project.create_bundle(label="release candidate", include_managed_folder_files=True)
project.list_bundles() # [{version, label, counts, ...}], newest first

# Download the stored artifact
b.download("/tmp/proj_v3.hsproj.zip")

# Activate (clone into a NEW project)
project.bundle(3).activate(target_name="Regional Copy", target_slug="regional_copy")

# Blue-green deploy / rollback an EXISTING project
project.bundle(3).deploy(target_project_id=57)

# Cross-install push
client.register_node(name="prod", base_url="https://prod.your-domain.com", api_key="hf_…")
client.list_nodes() # PAT never returned
project.bundle(3).push(node_id=1, target_name="Analytics", target_slug="analytics")

create_bundle returns a Bundle; project.bundle(version) returns a handle for an existing version. The SDK mirrors dataikuapi's bundle surface (export_bundle / list_exported_bundles / activate_bundle).

Where this sits operationally

Bundles are the project-level rung of the promotion ladder:

  • Templating — activate one master project into many independent copies in the same install.
  • Promote within an install — deploy a new version onto an existing project (e.g. moving a dev project's changes onto a prod project), with rollback to any earlier version.
  • Promote across installs — register the destination install as a node and push, framing the test → prod boundary as two registered installs rather than a node fleet. See Deployment Tiers for how Spaces map to installs across Cloud, Enterprise, and Self-Hosted.

For gating an individual recipe, dashboard, or agent before it goes live, use Governance and promote-to-live instead.

API reference

EndpointPermissionEffect
POST /api/projects/{id}/bundlesorg.adminSnapshot the project as version max+1.
GET /api/projects/{id}/bundlesproject.viewList versions, newest first.
GET /api/projects/{id}/bundles/{version}/downloadorg.adminStream the stored .hsproj.zip.
POST /api/projects/{id}/bundles/{version}/activateorg.adminClone the version into a new project.
POST /api/projects/{id}/bundles/{version}/deployorg.adminBlue-green replace an existing project (also rollback).
POST /api/projects/{id}/bundles/{version}/pushorg.adminPush the version to a registered remote node (additive).
GET /api/projects/{id}/bundles/activationsproject.viewActivation history for the project.
POST /api/nodesorg.adminRegister a remote install (base URL + PAT, encrypted at rest).
GET /api/nodesorg.adminList registered nodes (PAT never returned).
DELETE /api/nodes/{node_id}org.adminRemove a registered node.