Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .codex/skills/hack-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Use `hack` as the primary interface for local-first development.

- These instructions were generated by hack CLI v4.2.1; use the content revision and command behavior to assess freshness.
- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit.
- Content revision: `f4120f477794` (version alone is not a freshness guarantee).
- Content revision: `8f9bff2d631a` (version alone is not a freshness guarantee).

## Product boundary

Expand Down Expand Up @@ -160,7 +160,7 @@ Use `hack` as the primary interface for local-first development.
- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch.
- Target one with `--branch <name>` on up/open/logs/down (for example: `hack up --branch <name> --detach`).
- Linked worktrees pick a branch instance automatically (see Linked git worktrees).
- Containers in Compose-backed and native image projects receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective Caddy routes; authored `.hack/hack.project.json` projects do not receive them yet. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.
- Containers in Compose-file and authored `.hack/hack.project.json` projects, on the Compose and native backends, receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective routes; explicit project env and authored env values win. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.

## Project targeting

Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/hack.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is

- These instructions were generated by hack CLI v4.2.1; use the content revision and command behavior to assess freshness.
- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit.
- Content revision: `f4120f477794` (version alone is not a freshness guarantee).
- Content revision: `8f9bff2d631a` (version alone is not a freshness guarantee).

## Product boundary

Expand Down
38 changes: 31 additions & 7 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,12 @@ deliberately marks that exact Compose volume disposable. Hack deliberately does

### Runtime host metadata

Containers started by `hack up`, `hack restart`, and `hack run` for a `.hack/docker-compose.yml`
project receive the effective instance and public-route metadata. This applies to the default
Compose backend and to the native backend (`HACK_RUNTIME_BACKEND=native`). It keeps
server-generated links, OAuth callbacks, webhooks, and other browser-facing URLs isolated when the
same project runs in multiple worktrees. Both backends emit the same keys and document shape.
Containers started by `hack up`, `hack restart`, and `hack run` receive the effective instance and
public-route metadata. This applies to `.hack/docker-compose.yml` projects and to authored
`.hack/hack.project.json` projects, on the default Compose backend and on the native backend
(`HACK_RUNTIME_BACKEND=native`). It keeps server-generated links, OAuth callbacks, webhooks, and
other browser-facing URLs isolated when the same project runs in multiple worktrees. Every path
emits the same keys and document shape from the same shared builder.

| Variable | Value |
| --- | --- |
Expand Down Expand Up @@ -465,8 +466,31 @@ port; host variables never do. Native-specific limits:
removing the native HTTPS frontend when its port is not 443, changes the metadata without a
hostname change, so the retained graph refuses to start. Start it with its original HTTPS
frontend selection and port.
- Authored `.hack/hack.project.json` configuration does not deliver these variables yet, on either
backend.

For an authored `.hack/hack.project.json` project, the configuration compiler carries the values as
public `runtime` bindings (see the [compiler reference](reference/native-config-compiler.md)), so
they are reviewed with the rest of the plan and are never managed or secret values. The precedence
matches Compose-file projects: a managed project env value or any authored `environment` directive
for one of these keys (literal, default, reference, endpoint or `unset`) wins over the generated
value. The hosts come from the compiler's routing resolution:

- `HACK_DEV_HOST` is the resolved project origin's host (for example
`feature-x.demo.hack.local`). Without a routing resolution it is the compiler's generated
`<name>.hack.local`, prefixed with the selected branch when there is one.
- `HACK_ALIAS_HOST` is the host of the alias named by `routes.oauth_alias`, when set.
- Service URLs list each `routes.http` route for that service in route-name order, the route origin
before its aliases, so `HACK_SERVICE_URL` is the development origin.
- `HACK_BRANCH` is the routing resolution's branch, or the selected `--branch` instance when no
routing resolution exists.
- On the Compose backend `HACK_COMPOSE_PROJECT` is the instance's actual Compose project
(`hack-nc-...`), and URLs are portless. On the native backend it is `<name>` or
`<name>--<branch>`, as for native image projects, and URLs carry a selected non-443
`HACK_NATIVE_HTTPS_PORT`. Authored native graphs are not routed, so they have no alias and no
service URLs; the port appears only in `HACK_DEV_URL`.

Authored native `hack exec` sees the values stored in the running container, and native one-off
`hack run` uses the admitted service environment. A graph started before this release gains them
on its next `hack up` or `hack restart`.

Linked worktrees also inherit the project secret key automatically from the primary checkout
through the shared git common dir, so you don't need to copy `.hack.secret.key` by hand. Set
Expand Down
10 changes: 10 additions & 0 deletions docs/guides/native-candidate.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@ rest of this section describes the older Compose-file native frontend for
`.hack/hack.config.json` projects with a Compose file, which keeps its own
command coverage below.

Authored native `up` and `restart` deliver the same
[runtime host metadata](../cli.md#runtime-host-metadata) as native image projects.
The configuration compiler carries it as public `runtime` bindings in the reviewed
source selection, below managed values and authored directives, and the runtime
adds it to each workload's public environment, never to private delivery. A
selected non-443 `HACK_NATIVE_HTTPS_PORT` appears in `HACK_DEV_URL` (authored
native graphs are not routed, so they have no alias or service URLs); a partial HTTPS selection refuses before any input or runtime
work. `exec` observes the values stored in the running container, and one-off
`run` uses the admitted environment.

Explicit `HACK_RUNTIME_BACKEND=native` with absolute `HACK_NATIVE_BINARY` and
`HACK_NATIVE_HOME` selects native observations for normal `hack ps` and bounded
`hack logs SERVICE --no-follow`. They verify the project/branch run mapping
Expand Down
1 change: 1 addition & 0 deletions docs/reference/native-compose-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ portable semantic hashes, or write it into the authored configuration directory.
| Successful job dependency | `service_completed_successfully`; jobs share the generated Compose services namespace. |
| Exec readiness | `CMD` plus ordered argv; explicit shell source becomes `CMD /bin/sh -c`. |
| Literal, default, managed reference, unset | Deliver exactly the compiler's effective destination bindings; absent destinations stay absent and empty strings stay present. |
| Generated runtime host metadata | Public `runtime` bindings render as plain environment values. The renderer refuses one for a key outside the reserved `HACK_*` metadata set, or where an authored directive or a supplied managed value owns the key. |
| HTTP/HTTPS service endpoint | Explicit `protocol://service-name:port` using project-scoped Compose service DNS. |
| HTTP/HTTPS external binding | Explicit `protocol://hostname:port`; external loopback retains its container-context meaning and is not translated into a host gateway. |

Expand Down
7 changes: 7 additions & 0 deletions docs/reference/native-config-compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ For host declarations, metadata additionally includes
`host:{default?:{KEY:{scope,secret}},workloads:{TARGET:{KEY:{scope,secret}}}}`.
The default map is present exactly when requested, and workload maps exactly match
`host_env_targets.workloads`, including empty maps. Extra target authority refuses.
Optional `runtime:{WORKLOAD:{KEY:string}}` carries Hack-generated, non-secret
[runtime host metadata](../cli.md#runtime-host-metadata) for declared workloads.
Only the ten reserved `HACK_*` metadata keys are accepted, each value at most
64 KiB without NUL. The compiler reports them as public `{kind:"runtime",value}`
bindings below managed values and every authored directive, so explicit values
win. Host entries never receive them, and they do not change any hash in the
authored plan or local resolution. Protocol capability: `runtime_env_plan_version: 1`.
The compiler shares an 8 MiB output safety budget across the complete planning
envelope. It charges symbolic baselines before cloning them for each invocation,
plus authored directives and diagnostics before report growth. Accounting is
Expand Down
4 changes: 2 additions & 2 deletions examples/basic/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Use `hack` as the single interface for local-first runtime orchestration (compos
Integration freshness:
- These instructions were generated by hack CLI v4.2.1; use the content revision and command behavior to assess freshness.
- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit.
- Content revision: `f4120f477794` (version alone is not a freshness guarantee).
- Content revision: `8f9bff2d631a` (version alone is not a freshness guarantee).

Product boundary:
- Supported CLI surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon.
Expand Down Expand Up @@ -136,7 +136,7 @@ Branch instances (parallel envs):
- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch.
- Target one with `--branch <name>` on up/open/logs/down (for example: `hack up --branch <name> --detach`).
- Linked worktrees pick a branch instance automatically (see Linked git worktrees).
- Containers in Compose-backed and native image projects receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective Caddy routes; authored `.hack/hack.project.json` projects do not receive them yet. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.
- Containers in Compose-file and authored `.hack/hack.project.json` projects, on the Compose and native backends, receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective routes; explicit project env and authored env values win. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.

Project targeting:
- From repo root, commands use that project automatically.
Expand Down
4 changes: 2 additions & 2 deletions examples/basic/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Use `hack` as the single interface for local-first runtime orchestration (compos
Integration freshness:
- These instructions were generated by hack CLI v4.2.1; use the content revision and command behavior to assess freshness.
- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit.
- Content revision: `f4120f477794` (version alone is not a freshness guarantee).
- Content revision: `8f9bff2d631a` (version alone is not a freshness guarantee).

Product boundary:
- Supported CLI surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon.
Expand Down Expand Up @@ -136,7 +136,7 @@ Branch instances (parallel envs):
- Use a branch instance when you need two versions running at once (PR review, experiments, migrations) or want to keep a stable environment while testing another branch.
- Target one with `--branch <name>` on up/open/logs/down (for example: `hack up --branch <name> --detach`).
- Linked worktrees pick a branch instance automatically (see Linked git worktrees).
- Containers in Compose-backed and native image projects receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective Caddy routes; authored `.hack/hack.project.json` projects do not receive them yet. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.
- Containers in Compose-file and authored `.hack/hack.project.json` projects, on the Compose and native backends, receive `HACK_RUNTIME_METADATA` plus `HACK_DEV_URL`, `HACK_ALIAS_URL`, and current-service URL fields derived from effective routes; explicit project env and authored env values win. Use Compose DNS for internal traffic and this metadata for browser-facing links, OAuth callbacks, and webhooks.

Project targeting:
- From repo root, commands use that project automatically.
Expand Down
21 changes: 19 additions & 2 deletions packages/config-compiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,28 @@ and encoded-request bounds. Managed values, ciphertext and paths are unsupported
Successful parsing returns `ok: true` and the unchanged plan, semantic hash, local
resolution and declared namespace, plus `environment_plan`. That object contains
`plan_version: 1`, selection metadata, `complete`, selected `workloads`, `warnings`
and `diagnostics`. Bindings have one of three forms:
and `diagnostics`. Bindings have one of these forms:

- `managed`: symbolic `key`, winning `scope`, and `secret` flag;
- `literal`: the explicit public authored `value`;
- `default`: the explicit public fallback `value` used when no baseline key exists.
- `default`: the explicit public fallback `value` used when no baseline key exists;
- `endpoint`: a typed endpoint reference and target (see endpoint planning);
- `runtime`: a Hack-generated public runtime host metadata `value`.

Metadata may also include optional `runtime: {WORKLOAD: {KEY: value}}`: public,
Hack-generated runtime host metadata (`HACK_BRANCH`, `HACK_COMPOSE_PROJECT`,
`HACK_DEV_HOST`, `HACK_DEV_URL`, `HACK_ALIAS_HOST`, `HACK_ALIAS_URL`,
`HACK_SERVICE_NAME`, `HACK_SERVICE_URL`, `HACK_SERVICE_URLS` and
`HACK_RUNTIME_METADATA`). Only those closed keys are accepted, only for declared
workloads, with string values up to 64 KiB that contain no NUL. Anything else
refuses with a fixed request diagnostic that does not echo values. These values
are never managed or secret: they become `runtime` bindings for that selected
workload only where neither a managed baseline key nor an authored directive owns
the key. Managed values replace them, and every authored directive (literal,
default, reference, endpoint or unset) wins over them. Host process entries never
receive runtime bindings. The protocol advertises `runtime_env_plan_version: 1`;
an older compiler refuses the unknown field. Supplying runtime metadata changes
neither the authored plan nor the semantic or resolution hashes.

Managed baseline presence includes an empty managed value. `unset` removes a
binding. References always read the immutable owning baseline before authored
Expand Down
4 changes: 2 additions & 2 deletions packages/config-compiler/generated/native-config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Generated by hack-config-compiler; do not edit.
export type ManagedBindingMetadata = { scope: string, secret: boolean, };
export type EnvMetadata = { metadata_version: 1, overlay: string | null, overlay_exists: boolean, workloads: { [key in string]: { [key in string]: ManagedBindingMetadata } }, inactive_scopes: Array<string>, host?: HostMetadata, };
export type EnvMetadata = { metadata_version: 1, overlay: string | null, overlay_exists: boolean, workloads: { [key in string]: { [key in string]: ManagedBindingMetadata } }, inactive_scopes: Array<string>, host?: HostMetadata, runtime?: { [key in string]: { [key in string]: string } }, };
export type HostMetadata = { default?: { [key in string]: ManagedBindingMetadata }, workloads: { [key in string]: { [key in string]: ManagedBindingMetadata } }, };
export type HostEnvironmentPlan = { env_target: HostEnvTarget, bindings: { [key in string]: EnvironmentBinding }, };
export type EnvPlanRequest = { request_version: 1, project: string, primary_local?: string, checkout_local?: string, explicit_overlay?: string | null, global_domain?: string, explicit_domain?: string, branch?: string, env_metadata: EnvMetadata, };
export type EnvironmentBinding = { "kind": "managed", key: string, scope: string, secret: boolean, } | { "kind": "literal", value: string, } | { "kind": "default", value: string, } | { "kind": "endpoint", reference: EndpointReference, target: EndpointTarget, };
export type EnvironmentBinding = { "kind": "managed", key: string, scope: string, secret: boolean, } | { "kind": "literal", value: string, } | { "kind": "default", value: string, } | { "kind": "endpoint", reference: EndpointReference, target: EndpointTarget, } | { "kind": "runtime", value: string, };
export type FileConfig = { file: string, };
export type FileSecret = { file: string, } | { env_ref: string, };
export type FileKind = "config" | "secret";
Expand Down
Loading
Loading