1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use std::path::PathBuf;
use clap::{Args, Subcommand};
#[derive(Debug, Args)]
#[command(arg_required_else_help = true)]
pub struct PackArgs {
#[command(subcommand)]
pub command: Option<PackCommand>,
/// Entrypoint `.harn` file to pack. Transitive Harn modules and
/// non-Harn assets referenced by import directives under the
/// entrypoint's directory are bundled alongside it.
///
/// When `harn pack` is invoked without a subcommand, this positional
/// argument selects the build path; passing a subcommand (e.g.
/// `harn pack verify <bundle>`) routes to that subcommand instead.
#[arg(required = false)]
pub entrypoint: Option<PathBuf>,
/// Output `.harnpack` path. Defaults to the entrypoint stem with
/// the `.harnpack` extension next to the entrypoint.
#[arg(long, value_name = "PATH")]
pub out: Option<PathBuf>,
/// Read an existing `.harnpack` and re-emit it under the v2
/// manifest, preserving the prior bundle's id, name, version,
/// triggers, workflow graph, and prompt capsules. The new
/// `<entrypoint>` argument supplies the transitive-modules /
/// SBOM payload that v1 lacked.
#[arg(long, value_name = "OLD_BUNDLE")]
pub upgrade: Option<PathBuf>,
/// Sign the bundle hash and embed an Ed25519 signature in the manifest.
#[arg(
long,
default_value_t = false,
conflicts_with = "unsigned",
requires = "key"
)]
pub sign: bool,
/// Ed25519 private key PEM used with `--sign`.
#[arg(long, value_name = "PATH", requires = "sign")]
pub key: Option<PathBuf>,
/// Mark the bundle as unsigned. This still emits an OpenTrustGraph
/// release record at autonomy tier `suggest`.
#[arg(long, default_value_t = false)]
pub unsigned: bool,
/// Refuse to bundle modules whose path matches a built-in
/// secret-bearing glob (`.env`, `.env.*`, `*.pem`, `*.key`,
/// `credentials*`, anything under `secrets/`). The default behavior
/// matches the historical pack semantics: pack the full transitive
/// module set without any secret filtering. Pass `--exclude-secrets`
/// from CI or release pipelines that share bundles externally.
///
/// The same gate skips imported non-Harn assets that match the
/// secret heuristic and reports each skipped asset as a structured
/// JSON warning plus `manifest.metadata.skipped_assets`.
#[arg(long, default_value_t = false, conflicts_with = "include_secrets")]
pub exclude_secrets: bool,
/// Explicitly opt in to the default behavior: bundle every
/// transitive module without secret filtering. Useful in scripts
/// that want to be explicit about the bundle's contents instead of
/// relying on the default.
#[arg(long, default_value_t = false)]
pub include_secrets: bool,
/// Emit a `JsonEnvelope` summary instead of a human-readable
/// one-liner. Schema: `harn --json-schemas --command pack`.
#[arg(long, default_value_t = false)]
pub json: bool,
}
#[derive(Debug, Subcommand)]
pub enum PackCommand {
/// Expand a `.harnpack` bundle into a directory containing
/// `harnpack.json` plus the archive payload entries. Intended for
/// audit/debug workflows and tests that need to inspect or mutate a
/// bundle without depending on host `tar`/`zstd` binaries.
Unpack(PackUnpackArgs),
/// Reassemble a directory produced by `harn pack unpack` back into
/// a `.harnpack` bundle.
Repack(PackRepackArgs),
/// Verify a `.harnpack` bundle: check the embedded Ed25519
/// signature (if present), recompute the canonical bundle hash,
/// and compare each archive entry's BLAKE3 against the manifest's
/// recorded hashes. Exits non-zero on any mismatch.
Verify(PackVerifyArgs),
}
#[derive(Debug, Args)]
#[command(arg_required_else_help = true)]
pub struct PackUnpackArgs {
/// Path to the `.harnpack` archive to unpack.
pub bundle: PathBuf,
/// Output directory to create.
#[arg(long, value_name = "DIR")]
pub out: PathBuf,
/// Replace the output path if it already exists.
#[arg(long, default_value_t = false)]
pub force: bool,
}
#[derive(Debug, Args)]
#[command(arg_required_else_help = true)]
pub struct PackRepackArgs {
/// Directory containing `harnpack.json` and archive payload entries.
pub dir: PathBuf,
/// Output `.harnpack` path.
#[arg(long, value_name = "PATH")]
pub out: PathBuf,
/// Replace the output file if it already exists.
#[arg(long, default_value_t = false)]
pub force: bool,
}
#[derive(Debug, Args)]
#[command(arg_required_else_help = true)]
pub struct PackVerifyArgs {
/// Path to the `.harnpack` archive to verify.
pub bundle: PathBuf,
/// Accept bundles that carry no Ed25519 signature. Without this
/// flag, an unsigned bundle is treated as a verification failure.
#[arg(long, default_value_t = false)]
pub allow_unsigned: bool,
/// JSON trust policy describing the signer registry URL and
/// optional trusted signer allowlist to enforce during
/// verification.
#[arg(long, value_name = "PATH")]
pub trust_policy: Option<PathBuf>,
/// Require the bundle signer to resolve from the trusted signer
/// registry and, when `--trust-policy` supplies a
/// `trusted_signers` allowlist, appear in that allowlist too.
#[arg(long, default_value_t = false)]
pub require_trusted_signer: bool,
/// Cross-check SBOM package hashes against the archive payloads
/// they describe when the bundle format carries a corresponding
/// entry.
#[arg(long, default_value_t = false)]
pub strict: bool,
/// Emit a `JsonEnvelope` summary instead of a human-readable
/// one-liner. Schema: `harn --json-schemas --command "pack verify"`.
#[arg(long, default_value_t = false)]
pub json: bool,
}