world

package
v1.801.307 Latest Latest
Warning

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

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

Documentation

Overview

World plan enforcement contract.

This file is the ONE place cloud maps a Hanzo World plan to the limits that gate the /v1/world data plane. The values are NOT hardcoded here: they are resolved from the @hanzo/plans catalog (the single source of truth) via the canonical `world.*` entitlement vocabulary, so the numbers live in exactly one place — subscription.json in hanzoai/plans — and are read, never duplicated.

Contract (keys defined in hanzoai/plans entitlements.schema.json):

world.api_rate_limit  requests/minute  REST /v1/world/*        (-1 = unlimited)
world.mcp_rate_limit  requests/minute  MCP surface             (-1 = unlimited)
world.max_alerts      count            saved OSINT alert rules (-1 = unlimited)
world.model_api       boolean          /v1/world/model + SSE stream gate

Tier snapshot (sourced from the catalog, shown for reference only):

           api/min   mcp/min   alerts   model_api
world-free      60        30        3   denied
world-pro     6000      3000       -1   granted
world-team   30000     15000       -1   granted
world-enter     -1        -1       -1   granted (contact sales)

ENFORCEMENT SEAM. Two callers consume this contract:

  • this subsystem's /v1/world/{news,pipeline,stream} handlers (rate limiting + the SSE model_api gate), and
  • the /v1/world/model planet-scale engine (feat/world-model-engine), whose pro-tier gate calls ResolveWorldLimits and checks WorldLimits.ModelAPI.

Both MUST resolve through ResolveWorldLimits so the policy stays single-sourced.

FOLLOW-UP (documented, coordinated — not built here). Per-request ENFORCEMENT needs the caller's effective plan id (org -> plan), which is a subscription lookup owned by the billing plane (commerce /v1/billing/subscriptions); the gateway principal carries org/project but no plan claim today. Until that org->plan resolver lands, ResolveWorldLimits takes an explicit plan id and GET /v1/world/limits echoes any plan's limits so agents/dashboards self-config against the live catalog. Wiring the rate limiter into the handlers is the rollout step, coordinated with the feat/world-model-engine gate to avoid two enforcement paths.

Package world mounts the Hanzo Cloud "World" news data plane: a per-org, per-project intelligence feed that normalizes GDELT + host-allowlisted RSS/Atom into one NewsItem stream, applies the project's keyword/region/source filter, and serves it over REST + SSE. It is the Go backend for the World monitor frontend (hanzoai/world), replacing that app's Vercel edge functions (api/gdelt-doc.js, api/rss-proxy.js) with an org-scoped, in-binary subsystem.

Surface (all org/project-scoped; /v1 only):

GET /v1/world/news       merged, filtered, freshest-first feed -> {items:[…]}
GET /v1/world/pipeline   per-project pipeline config (read)    -> {…}
PUT /v1/world/pipeline   per-project pipeline config (write)   -> {…}
GET /v1/world/stream     SSE live refresh (ZAP-native)         -> event: news

TENANT ISOLATION is enforced SERVER-SIDE on every request. The (org, project) tuple is principal.Org + principal.Project (the values SanitizeIdentity minted from the VALIDATED bearer, HIP-0026) — never a query param, body, or client header. Every store statement carries `WHERE org=? AND project=?`; the SSE bus filters on org and the stream loop drops other projects. A request with no validated principal is a 403.

SECURITY. The RSS fetcher is an SSRF boundary: a feed URL's host must be in the ported rss-proxy.js allowlist (allowlist.go), enforced at BOTH the PUT write boundary and at fetch time, including on redirect targets (client CheckRedirect).

Order 142 binds /v1/world/* ahead of the AI /v1/* catch-all (150).

Index

Constants

This section is empty.

Variables

View Source
var FreeWorldLimits = WorldLimits{APIRateLimit: 60, MCPRateLimit: 30, MaxAlerts: 3, ModelAPI: false}

FreeWorldLimits is the fail-closed floor: the limits applied when no plan can be resolved (unknown plan, or the catalog is unreachable). It equals the world-free tier so a catalog outage degrades to exactly Free — never above, and never granting the model/stream API.

Functions

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount registers the world surface on app per HIP-0106.

func Shutdown

func Shutdown() error

Shutdown closes the SSE bus (unblocking every open stream) and the pipeline store. Idempotent — safe to call when nothing is mounted.

Types

type Filters

type Filters struct {
	Regions  []string `json:"regions"`
	Keywords []string `json:"keywords"`
	Sources  []string `json:"sources"`
}

Filters narrows the merged news stream. Each axis is an OR-set of terms; a non-empty axis is an AND predicate against the item (empty axis = pass-through). Keywords ALSO seed the GDELT queries, so it is both a fetch input (fresh keyword-matched articles) and a post-merge filter.

type NewsItem

type NewsItem struct {
	Source  string `json:"source"`
	Title   string `json:"title"`
	Link    string `json:"link"`
	PubDate string `json:"pubDate"` // RFC3339 UTC, or "" when the upstream gave no parseable date
	Lang    string `json:"lang,omitempty"`
	Image   string `json:"image,omitempty"`
	Tone    string `json:"tone,omitempty"`
}

NewsItem is the normalized, source-agnostic shape every upstream (GDELT, RSS, Atom) is projected into and the wire contract for GET /v1/world/news.

type Pipeline

type Pipeline struct {
	Org       string
	Project   string
	Feeds     []string
	Filters   Filters
	CreatedAt int64
	UpdatedAt int64
}

Pipeline is the persisted per-(org,project) news pipeline config. Feeds are host-allowlisted RSS/Atom URLs; Filters narrows the merged result.

type PipelineStore

type PipelineStore struct {
	// contains filtered or unexported fields
}

PipelineStore is the world pipeline metastore over one SQLite file ({DataDir}/world.db). Tenancy is the (org, project) key.

func (*PipelineStore) Close

func (s *PipelineStore) Close() error

Close closes the underlying database.

func (*PipelineStore) Get

func (s *PipelineStore) Get(ctx context.Context, org, project string) (Pipeline, error)

Get returns the persisted pipeline for (org, project), or errNotFound when the org has never written a pipeline for that project (the caller then serves the default pipeline).

func (*PipelineStore) Put

func (s *PipelineStore) Put(ctx context.Context, p Pipeline) (Pipeline, error)

Put upserts the feeds + filters for (org, project). It is idempotent on the composite key and preserves created_at across updates.

type WorldLimits

type WorldLimits struct {
	APIRateLimit int  // world.api_rate_limit
	MCPRateLimit int  // world.mcp_rate_limit
	MaxAlerts    int  // world.max_alerts
	ModelAPI     bool // world.model_api — /v1/world/model + SSE stream access
}

WorldLimits is the resolved World plan contract for one caller: the enforcement inputs both the /v1/world handlers and the /v1/world/model gate read. Rate limits are requests/minute; -1 means unlimited.

func ResolveWorldLimits

func ResolveWorldLimits(ctx context.Context, planID string) (WorldLimits, error)

ResolveWorldLimits resolves the World limits for a plan id straight from the @hanzo/plans catalog. An empty id resolves the free tier. On any resolution error it returns FreeWorldLimits together with the error so callers can log the degradation while still failing closed.

func WorldLimitsFromEntitlements

func WorldLimitsFromEntitlements(ent map[string]any) WorldLimits

WorldLimitsFromEntitlements maps a resolved `world.*` entitlement block onto WorldLimits. Pure and total: any key absent from the block keeps the Free-floor value, so a partial catalog can only ever narrow access. Exported so the /v1/world/model gate can derive limits from an already-resolved entitlement map without a second catalog round-trip.

Jump to

Keyboard shortcuts

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