Documentation
¶
Overview ¶
Package projects is the Hanzo Cloud projects control plane: the ONE org-scoped store of buildable/deployable sites, shared by every surface that shows a user's projects.
Why it exists: hanzo.app (the builder) and console.hanzo.ai (the Projects module) must show the SAME projects for the same org. They do, because both call this one /v1/projects surface through the gateway, which mints the org (X-Org-Id) from the validated IAM JWT (HIP-0111). There is no second copy of project state anywhere — this SQLite-backed store is the source of truth; the builder keeps only per-project working state (chat, draft files) in Hanzo Base.
Surface (all org-scoped; see CONTRACT.md — the published shape console consumes):
POST /v1/projects create GET /v1/projects list (org) GET /v1/projects/:slug get PATCH /v1/projects/:slug update DELETE /v1/projects/:slug delete (+ purge S3 site) POST /v1/projects/:slug/deploy deploy (tar body | git json) GET /v1/projects/:slug/deployments deploy history GET /v1/projects/:slug/deployments/:id one deployment POST /v1/projects/:slug/deployments/:id/complete CI completion hook
Sites surface (the surface-agnostic deploy_site capability, shared with agents):
POST /v1/sites generate a responsive site from a brief + deploy POST /v1/sites/deploy deploy a raw file manifest (the deploy_site tool) GET /v1/sites list the org's live sites
Deploy pipeline: a deploy uploads the built static site to OUR S3 (CLOUD_PROJECTS_BUCKET on s3.hanzo.ai) under "<org>/<slug>/", marks the bucket public-read, and records a live URL. The hanzoai/static container (the static-app image) serves the same bucket behind the gateway for a pretty host; GitHub export is an optional second step that never blocks going live.
Index ¶
- func Mount(app *zip.App, deps cloud.Deps) error
- func SetDeployObserver(o DeployObserver)
- func Shutdown() error
- type DeployObserver
- type Deployment
- type Project
- type Store
- func (s *Store) BindHost(ctx context.Context, host, org, slug string, now int64) error
- func (s *Store) Close() error
- func (s *Store) CreateProject(ctx context.Context, p Project) error
- func (s *Store) DeleteProject(ctx context.Context, org, slug string) (Project, bool, error)
- func (s *Store) GetDeployment(ctx context.Context, org, projectID, id string) (Deployment, error)
- func (s *Store) GetProject(ctx context.Context, org, slug string) (Project, error)
- func (s *Store) InsertDeployment(ctx context.Context, d Deployment) error
- func (s *Store) ListDeployments(ctx context.Context, org, projectID string) ([]Deployment, error)
- func (s *Store) ListHostsForProject(ctx context.Context, org, slug string) ([]string, error)
- func (s *Store) ListProjects(ctx context.Context, org string) ([]Project, error)
- func (s *Store) NextVersion(ctx context.Context, projectID string) (int, error)
- func (s *Store) ProjectOwnership(ctx context.Context, org, idOrSlug string) (mine, other bool, err error)
- func (s *Store) ResolveHost(ctx context.Context, host string) (Project, error)
- func (s *Store) UnbindHost(ctx context.Context, host, org, slug string) error
- func (s *Store) UpdateDeployment(ctx context.Context, d Deployment) error
- func (s *Store) UpdateProject(ctx context.Context, p Project) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Mount ¶
Mount wires the projects surface onto app per HIP-0106. Complex flavour: it keeps a package global (mounted) for Shutdown and registers cross-package resolvers, so it constructs the Service value directly rather than through cloud.Mount.
func SetDeployObserver ¶ added in v1.801.30
func SetDeployObserver(o DeployObserver)
SetDeployObserver registers the package-level deploy observer (nil clears it). It is safe for concurrent use; the last writer wins. Wiring is intentionally package-level (not per-service) because there is one mounted projects surface per binary and the sessions lane wires the observer at startup.
Types ¶
type DeployObserver ¶ added in v1.801.30
type DeployObserver interface {
OnDeploy(ctx context.Context, org, slug, url, deploymentID string)
}
DeployObserver receives one notification whenever a site goes live. It is the seam the (separately-landed) agent-sessions lane hooks to record a "site deployed + URL" session event, WITHOUT projects taking any hard dependency on that lane: projects only ever calls this interface. The default is nil — a no-op — so a deployment that never registers an observer is unaffected.
type Deployment ¶
type Deployment struct {
ID string
ProjectID string
Org string
Version int
Status string
Source string
Commit string
LiveURL string
Bucket string
Prefix string
Files int
Bytes int64
Message string
CreatedAt int64
UpdatedAt int64
}
Deployment is one deploy attempt for a project, versioned monotonically per project. A deploy moves through queued→building→uploading→live (or →error); the upload path (tar body) lands directly in "live", the git/CI path starts "queued" and is flipped by the CI completion call.
type Project ¶
type Project struct {
ID string
Org string
Slug string
Name string
Description string
RepoURL string
RepoBranch string
RepoProvider string
Framework string
Status string
LiveURL string
Bucket string
CurrentDeploy string
// CacheControl is the per-project override for the HTML/document Cache-Control
// header applied to deployed objects (and honored by the site server). Empty =
// the honest default (public, max-age=60, s-maxage=86400). Content-hashed
// assets are always immutable regardless of this override.
CacheControl string
// LastPurgeAt is the unix time of the last successful (or attempted) Cloudflare
// edge purge for this site. Surfaced on the API so a console can show cache freshness.
LastPurgeAt int64
CreatedAt int64
UpdatedAt int64
}
Project is the org-scoped, canonical record of a buildable/deployable site. It is the SAME record whether read from hanzo.app (the builder) or console.hanzo.ai (the Projects module): org isolation is the org column, enforced at the query layer, and the gateway-minted X-Org-Id selects the org. Repo fields are flat columns here; the HTTP surface nests them under "repo" (see projects.go). It never stores a secret.
Distinct from tracker.Project (not a duplicate): this is the Slug-keyed deployable site; a tracker.Project is a KEY-prefixed issue team that lives INSIDE one of these (the IAM/deploy project is the tracker's tenant boundary).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the projects metadata database. ONE SQLite file ({DataDir}/projects.db) holds every org's records; org-scoping is the org column. MaxOpenConns(1) serializes writes against the file lock without busy retries.
func (*Store) BindHost ¶
BindHost claims the global public host for (org, slug), first-come. It is idempotent for the SAME owner (a re-deploy just refreshes updated_at). If host is already bound to a DIFFERENT project it returns errHostTaken WITHOUT overwriting — a losing bind must never hijack another org's live subdomain.
func (*Store) CreateProject ¶
CreateProject inserts one project. A UNIQUE(org,slug) violation surfaces as errConflict.
func (*Store) DeleteProject ¶
DeleteProject removes a project and all its deployment rows. Reports whether a project row was deleted.
func (*Store) GetDeployment ¶
GetDeployment returns one deployment scoped to (org, project, id).
func (*Store) GetProject ¶
GetProject returns the project for (org,slug) or errNotFound.
func (*Store) InsertDeployment ¶
func (s *Store) InsertDeployment(ctx context.Context, d Deployment) error
InsertDeployment writes one deployment row.
func (*Store) ListDeployments ¶
ListDeployments returns deployments for a project, newest version first.
func (*Store) ListHostsForProject ¶ added in v1.786.165
ListHostsForProject returns every public host bound to (org, slug), oldest first. It powers GET .../domains so a console/user can see which hostnames the site serves — its `<slug>.hanzo.app` subdomain plus any bound custom domains.
func (*Store) ListProjects ¶
ListProjects returns every project for org, most-recently-updated first.
func (*Store) NextVersion ¶
NextVersion returns the next monotonic deploy version for a project (1-based).
func (*Store) ProjectOwnership ¶
func (s *Store) ProjectOwnership(ctx context.Context, org, idOrSlug string) (mine, other bool, err error)
ProjectOwnership reports whether a project addressed by id-or-slug is owned by org (mine) and/or by some OTHER org (other) — the cross-org impersonation signal the identity trust boundary (cloud.SanitizeIdentity) uses to refuse a forged X-Project-Id. Matched by BOTH slug and id so the check holds whichever addressing the caller used, in one indexed round trip. Org isolation is the org column, exactly as everywhere else in this store.
func (*Store) ResolveHost ¶
ResolveHost returns the project a public host is bound to, joining the global site_hosts binding to the org-scoped project. This is the authoritative slug→project resolution the site server uses; the org and bucket come ONLY from here, never from the request. Missing binding OR missing project ⇒ errNotFound.
func (*Store) UnbindHost ¶
UnbindHost releases a host binding, but only the row owned by (org, slug) — so deleting project A can never drop project B's host even if they somehow shared a row (they cannot, but the scoping makes the guarantee explicit).
func (*Store) UpdateDeployment ¶
func (s *Store) UpdateDeployment(ctx context.Context, d Deployment) error
UpdateDeployment overwrites the mutable fields of a deployment (status flow).