hack.foo
Reference

Project and env reports

Public report shapes at 91b14bf0abb17602142425de0d02628a3e8a2615. This page describes CLI projections, not private daemon/compiler/guest protocols. See Command and output contracts for envelope and exit semantics. v4 has no MCP env.keys/env.explain registrations. Use the version-specific CLI inspection contract rather than assuming v5 tools exist.

Project inventory

hack projects --json and status inventory emit a bare report. --summary selects the summary projection; --meta adds optional metadata. Missing or null observations mean unavailable/unknown, not proof that resources or processes do not exist. Runtime health in report data is separate from exit status.

SelectionReport effect
Default full listingprojects[] contains serialized project views, runtime observations, sessions, worktrees and lifecycle descriptions
--summarydetail_level is summary; rows contain identity/status and counts instead of full nested runtime views
--metameta is present on each row; it may be null

Project metadata types

The following declarations are copied from the pinned public metadata producers. Optional properties may be omitted. Nullable properties are present but may be null. Raw metadata contains repository paths and command descriptions; it is not a redacted sharing format. Ownership shared/broker names are retained compatibility metadata, not hosted team management or an access-control API.

Pinned src/lib/project-meta.ts.

export type GitWorktreeMeta = {
  readonly path: string;
  readonly head: string | null;
  readonly branch: string | null;
  readonly detached: boolean;
};

export type GitMeta = {
  readonly isRepo: boolean;
  readonly head: string | null;
  readonly branch: string | null;
  readonly detached: boolean | null;
  readonly dirty: boolean | null;
  readonly localBranchCount: number | null;
  readonly worktrees: readonly GitWorktreeMeta[] | null;
  readonly error: string | null;
};

export type HackBranchesMeta = {
  readonly path: string;
  readonly parseError: string | null;
  readonly branches: readonly BranchEntry[];
};

export type EnvVarMeta = {
  readonly key: string;
  readonly required: boolean;
  readonly source: HackEnvSource;
  readonly services: readonly string[] | null;
  readonly description?: string;
  readonly resolvedFrom:
    | "dotenv"
    | "process"
    | "keychain"
    | "portable_backend"
    | null;
  readonly hasValue: boolean;
};

export type EnvMeta = {
  readonly contractPath: string;
  readonly contractExists: boolean;
  readonly contractParseError: string | null;
  readonly vars: readonly EnvVarMeta[];
  readonly missingRequired: readonly string[];
};

export type SessionsMeta = {
  readonly sessions: readonly MuxSession[];
};

export type ComposeBuildServiceMeta = {
  readonly service: string;
  readonly build: boolean;
  readonly context: string | null;
  readonly dockerfile: string | null;
  readonly dockerfilePath: string | null;
  readonly dockerfileExists: boolean | null;
};

export type ComposeBuildMeta = {
  readonly services: readonly ComposeBuildServiceMeta[];
};

export type ProjectMeta = {
  readonly ownership: ProjectOwnershipConfig | null;
  readonly configError: string | null;
  readonly git: GitMeta;
  readonly hackBranches: HackBranchesMeta;
  readonly env: EnvMeta;
  readonly sessions: SessionsMeta;
  readonly composeBuild: ComposeBuildMeta;
};

Pinned src/lib/branches.ts.

export type BranchEntry = {
  readonly name: string;
  readonly slug: string;
  readonly note?: string;
  readonly created_at?: string;
  readonly last_used_at?: string;
};

Pinned src/mux/mux-backend.ts.

export type MuxBackendName = "tmux" | "zellij";

export type MuxSession = {
  readonly backend: MuxBackendName;
  readonly name: string;
  readonly attached: boolean | null;
  readonly path: string | null;
  readonly windows: number | null;
  readonly createdAt: string | null;
};

Pinned src/lib/hack-env.ts.

export type HackEnvSource = "plain_env" | "keychain";

Pinned src/lib/project.ts.

export type ProjectOwnershipMode = "local" | "shared";

export type ProjectOwnershipOwnerType = "user" | "team" | "organization";

export type ProjectOwnershipManager = "local" | "broker";

export interface ProjectOwnershipConfig {
  readonly mode: ProjectOwnershipMode;
  readonly ownerType: ProjectOwnershipOwnerType;
  readonly ownerId: string | null;
  readonly managedBy: ProjectOwnershipManager;
}

Serialized runtime observations

The public runtime projection uses snake_case names and arrays, not the internal Map representation. It contains project:string, working_dir:string|null and services[]. Each service has service:string and containers[]. Container fields: id/state/status/name/ports:string; working_dir/image:string|null; labels:string-map|null; mounts[] and networks[]. Mount fields: type/source/destination/mode:string, name:string|null, rw:boolean|null. Network fields: name:string, ip_address/gateway:string|null, aliases:string[]. Labels and engine state strings remain open values.

The raw ps items[] engine report is a different surface from this normalized projects[].runtime projection. Do not infer that an open engine report makes Hack-owned wrapper fields unknowable.

Sessions and lifecycle in full project rows

Full-row sessions[] uses name:string, backend:tmux|zellij, source:hack|external, attached:boolean, path:string|null, windows:number|null and created_at:number|null. This numeric creation observation is distinct from the metadata MuxSession.createdAt string|null above; do not silently convert one contract into the other.

worktrees is null or an array of path:string, branch:string|null and last_seen_at:string. lifecycle is null or an object with up_before/up_after/down_before/down_after arrays and processes[]. Hook rows carry name:string|null, command:string, cwd:string|null and service:string; persistent:true is emitted only when true. Process rows require name/command/service:string and cwd:string|null.

Environment list reports

Raw CLI env reports can contain values. Default secret masking does not make an entire report value-free: plaintext values still appear. Use a value-free inspection interface for agent metadata, and avoid copying raw env reports into logs or messages. These examples describe shapes only and contain no real values.

The legacy contract and managed YAML contract are separate variants. Both use vars[] in raw CLI output. MCP env.keys, where available, returns a narrower keys[] projection and withholds raw stdout/stderr.

FieldLegacy contractManaged YAML
projectstringstring
env_selectionrequested/default/effective/overlay_path:string or null; overlay_exists:booleansame public field names
formatomitted in legacy listproject_env_config_v1
statusclassification plus summary/detail stringsYAML classification plus summary/detail strings
storagelegacy contract/local/portable compatibility reportcanonical YAML/project-key report; contract member is not added by this builder
warningsarray of warning recordsnot emitted by modern list builder
varsarray of resolved variable recordsarray of selected-scope records
missing_requiredstring[]empty array in modern list builder
files/scopes/unknown_scopes/selected_scopeomittedstring[]/string[]/string[]/string
materialized_keysomittedsorted string[] unless values were explicitly requested
materializedomittedstring map only on explicit value-output path

Variable records

FieldLegacyManaged YAML
keystringstring
requiredboolean from contractfalse
sourceplain_env or keychainplain_env or keychain (secret marker)
servicesstring[] or nullwinning non-global scope as one-element array, otherwise null
resolved_fromdotenv/process/keychain/portable_backend or nullproject_env_config
storageHackEnvValueStorageJsonkind/plaintext-or-secret, backend/project_env_config, location:string, mode/yaml, trust_model:string, classification
valuestring or null; secret default mask is ***string; secret default mask is ***

The source field is a compatibility label: keychain on a managed YAML record marks a secret; it does not mean that YAML ciphertext moved to OS keychain storage. Files and scopes are selected by the matching version parser. v4 rejects YAML null; v5 uses it as a removal marker.

Legacy storage and classification types

These type names describe serialized report metadata. A retained backend/classification value is not qualification of every backend or hosted shared-state feature. Modern YAML storage has its own concrete projection described above.

export type HackEnvSharedState =
  | "local_only"
  | "plaintext_compatible"
  | "portable_bundle"
  | "broker_managed";
export type HackEnvJsonClassification = {
  readonly trust_model: string;
  readonly custody: string;
  readonly portability: string;
  readonly shared_state: HackEnvSharedState;
};
export type HackEnvValueStorageJson = {
  readonly kind: "plaintext" | "secret";
  readonly backend: string;
  readonly location: string;
  readonly mode: string;
  readonly trust_model: string;
  readonly classification: HackEnvJsonClassification;
};
export type HackEnvStorageJson = {
  readonly contract: {
    readonly path: string;
    readonly trust_model: string;
    readonly classification: HackEnvJsonClassification;
  };
  readonly local_plaintext: {
    readonly path: string;
    readonly exists: boolean;
    readonly trust_model: string;
    readonly mirrored_to_backend: boolean;
    readonly fallback: {
      readonly enabled: boolean;
      readonly source: string;
      readonly trust_model: string;
      readonly classification: HackEnvJsonClassification;
    };
    readonly classification: HackEnvJsonClassification;
  };
  readonly local_secrets: {
    readonly backend: string;
    readonly location: string;
    readonly mode: string;
    readonly provider: string | null;
    readonly trust_model: string;
    readonly classification: HackEnvJsonClassification;
  };
  readonly portable_state: {
    readonly status: string;
    readonly trust_model: string;
    readonly message: string;
    readonly classification: HackEnvJsonClassification;
  };
  readonly compatibility_mode: {
    readonly plaintext_target: string;
    readonly secret_backend: string;
    readonly plaintext_mirrored_to_backend: boolean;
    readonly summary: string;
  };
};
export type HackEnvAggregateStatusJson = HackEnvJsonClassification & {
  readonly summary: string;
  readonly detail: string;
};

Legacy warnings

Warnings contain kind:overlay_plaintext_ignored_for_secret_contract, key:string, overlay_path:string, services:string[]|null and message:string. This is a report of contract/source mismatch; it is not a secret value. Missing-required names are reported separately.

Scope and provenance

Project view serializer, runtime serializer, env output builders, and env storage serializers are the authority for these projections. This documents public fields from source, not successful runtime, cloud or client qualification. Unknown application map keys and external engine values are intentionally open; owned report fields above are not treated as open merely because the JSON emitter returns Record<string, unknown>.

Applying environment changes

hack env apply is a service restart wrapper. It does not return the env-list report above: it forwards restart, a concrete service, --path and selected env/JSON arguments, then returns the child exit code/output. Service/JSON forms remain subject to that version and backend admission. This is not an atomic multi-service env transaction.

v4 trims the service and refuses an empty name or global; it forwards a truthy env option. next.5 uses stricter service/selection parsers rather than widening this contract.

Listing field types

--summary and --timings require --json. --summary cannot be combined with --meta or --details. Timings are a separate numeric diagnostic record on stderr; they do not change stdout into a timing envelope.

Top-level fieldType / presence
generated_atstring timestamp
filterstring or null
include_global/include_unregistered/include_metaboolean
detail_levelsummary when summary selected; otherwise omitted
runtime_okboolean
runtime_error/runtime_checked_at/runtime_last_ok_at/runtime_reset_at/runtime_reset_summary/runtime_last_repair_at/runtime_repair_action/runtime_next_stepstring or null
runtime_reset_countnumber
runtime_reset_changesstring[]
runtime_repair_outcomestabilized, manual_action_required or null
projectsproject row array

Summary rows contain project_id:string|null; name:string; repo_root/dev_host:string|null; status:running/stopped/missing/unregistered/unknown; runtime_status below; defined_service_count:number|null; host_process_count/container_count/running_container_count/branch_count/session_count:number. Counts describe the gathered snapshot, not ongoing ownership or liveness.

runtime_status is running/stopped/missing/unknown/not_configured in v4.

Full and summary rows are separate projections. Full rows share project_id/name/repo_root/dev_host/status/runtime_status, but omit the summary-only defined_service_count, host_process_count, container_count, running_container_count, branch_count and session_count. Full rows contain project_dir:string|null; ownership:compatibility object|null with mode/owner_type/owner_id/managed_by; defined_services/extensions_enabled/features:string[]|null; service_hosts:string-to-string-array map|null; runtime_configured:boolean|null; normalized runtime|null; branch_runtime array of branch:string plus runtime; sessions/worktrees/lifecycle as described above; kind:registered/unregistered. The ownership data is descriptive, not authorization for hosted features.

Managed YAML classification values

Managed YAML does not use the legacy classification enum above. The exact classification groups emitted by the modern builder are:

Grouptrust_modelcustodyportabilityshared_state
YAML aggregate/portable staterepo_managed_env_configrepo_tracked_env_configportable_with_out_of_band_keyrepo_managed_overlay
Compatibility plaintextcompatibility_outputlocal_plaintext_filelocal_onlycompatibility_only
Local project keylocal_secret_keylocal_secret_keylocal_onlylocal_only

Source: v4.2.4 · docs/site/reference/project-env-reports.md.

On this page