projects

package
v1.801.108 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: Apache-2.0 Imports: 32 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func Mount

func Mount(app *zip.App, deps cloud.Deps) error

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.

func Shutdown

func Shutdown() error

Shutdown closes the projects store. Idempotent. Mirrors the provisioning Shutdown contract so the serve layer releases subsystem resources uniformly.

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

func (s *Store) BindHost(ctx context.Context, host, org, slug string, now int64) error

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) Close

func (s *Store) Close() error

Close closes the underlying database.

func (*Store) CreateProject

func (s *Store) CreateProject(ctx context.Context, p Project) error

CreateProject inserts one project. A UNIQUE(org,slug) violation surfaces as errConflict.

func (*Store) DeleteProject

func (s *Store) DeleteProject(ctx context.Context, org, slug string) (Project, bool, error)

DeleteProject removes a project and all its deployment rows. Reports whether a project row was deleted.

func (*Store) GetDeployment

func (s *Store) GetDeployment(ctx context.Context, org, projectID, id string) (Deployment, error)

GetDeployment returns one deployment scoped to (org, project, id).

func (*Store) GetProject

func (s *Store) GetProject(ctx context.Context, org, slug string) (Project, error)

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

func (s *Store) ListDeployments(ctx context.Context, org, projectID string) ([]Deployment, error)

ListDeployments returns deployments for a project, newest version first.

func (*Store) ListHostsForProject added in v1.786.165

func (s *Store) ListHostsForProject(ctx context.Context, org, slug string) ([]string, error)

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

func (s *Store) ListProjects(ctx context.Context, org string) ([]Project, error)

ListProjects returns every project for org, most-recently-updated first.

func (*Store) NextVersion

func (s *Store) NextVersion(ctx context.Context, projectID string) (int, error)

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

func (s *Store) ResolveHost(ctx context.Context, host string) (Project, error)

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

func (s *Store) UnbindHost(ctx context.Context, host, org, slug string) error

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).

func (*Store) UpdateProject

func (s *Store) UpdateProject(ctx context.Context, p Project) error

UpdateProject overwrites the mutable fields of an existing project. The caller reads-modifies-writes the whole Project; org+slug+id+created_at are immutable.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL