@zuke/signpath
`@zuke/signpath` — Authenticode signing through
@zuke/signpath on JSR ↗ 20 symbols
Symbols
class SignPathApiError extends Error SignPath refused a call: a non-2xx status that is not a transient one the transport retried.
try {
await SignPathTasks.status((s) => s.signingRequestId(id));
} catch (error) {
if (error instanceof SignPathApiError && error.status === 401) {
// the API token is wrong or expired
}
}| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
method | method: string | The HTTP method of the failing call. |
path | path: string | The endpoint path, such as /v1/<org>/SigningRequests (never the host). |
status | status: number | The HTTP status of the answer. |
detail | detail: string | undefined | What SignPath said, when it said anything: the error, detail, title or message of a JSON answer, or the start of a plain-text one. Scrubbed of credentials and control characters, and capped in length. |
class SignPathConnectionSettings The connection half of every SignPath call's settings.
| Member | Signature | Doc |
|---|---|---|
organizationId | organizationId(id: string): this | The SignPath organization id — the GUID in the web app's URLs and under *Organization settings*. Defaults to the SIGNPATH_ORGANIZATION_ID environment variable. |
apiToken | apiToken(token: string): this | The API token of a SignPath user with submitter permission on the project's signing policy, sent as Authorization: Bearer <token>. Defaults to the SIGNPATH_API_TOKEN environment variable. The token never reaches a progress line or an error message: every message this package builds is scrubbed of it. Declare it in the build as a parameter(...).secret() so Zuke masks it in its own output too. |
apiUrl | apiUrl(url: string): this | The REST API's base URL, https://app.signpath.io/Api by default; the version and organization (/v1/<org>/…) are appended. It carries the token, so it must be https: unless it is loopback or ZUKE_ALLOW_INSECURE_URL is set. The web app's links in errors and progress lines use the same origin (<origin>/Web/<org>/…). |
requestTimeout | requestTimeout(duration: string | number): this | Abort one API call after this long ("30s", or ms); default one minute. |
downloadTimeout | downloadTimeout(duration: string | number): this | Abort a signed-artifact download that has not finished after this long ("10m", or ms); default five minutes. |
maxDownloadBytes | maxDownloadBytes(bytes: number): this | Refuse a signed artifact larger than this many bytes; default 2 GiB. |
retries | retries(count: number): this | Retry a transient failure this many times (default 3), with a doubling delay from one second. A status poll or a download is retried after a network error, a timeout, or HTTP 429, 502, 503 or 504; a submission only after 429 or 503, the answers that say the request was not processed, so a retry cannot create a second signing request. |
fetch | fetch(fetcher: fetch): this | Send with this fetch instead of the global one — the seam tests use. |
readEnv | readEnv(reader: unknown): this | Read environment variables through reader instead of Deno.env. |
sleep | sleep(sleeper: unknown): this | Wait through sleeper instead of a timer — the seam tests use. |
now | now(clock: unknown): this | Read the time from clock (epoch ms) instead of Date.now. |
log | log(sink: unknown): this | Send progress lines to sink instead of console.log. |
organizationId_? | organizationId_?: string | The organization id (set by organizationId); else read from the environment. |
apiToken_? | apiToken_?: string | The API token (set by apiToken); else read from the environment. |
apiUrl_ | apiUrl_: string | The REST API's base URL (set by apiUrl). |
requestTimeout_ | requestTimeout_: number | The per-call timeout in ms (set by requestTimeout). |
downloadTimeout_ | downloadTimeout_: number | The signed-artifact download timeout in ms (set by downloadTimeout). |
maxDownloadBytes_ | maxDownloadBytes_: number | The signed-artifact size cap in bytes (set by maxDownloadBytes). |
retries_ | retries_: number | How many times a transient failure is retried (set by retries). |
fetch_ | fetch_: fetch | The fetch to use (set by fetch); the global one by default. |
readEnv_ | readEnv_: unknown | The environment reader (set by readEnv). |
sleep_ | sleep_: unknown | How the task waits between polls and retries (set by sleep). |
now_ | now_: unknown | The clock, in epoch ms (set by now). |
log_ | log_: unknown | Where progress lines go (set by log); console.log by default. |
class SignPathDownloadSettings extends SignPathStatusSettings Settings for SignPathTasks.download: the connection, the signing request's id, and where to write the signed artifact.
| Member | Signature | Doc |
|---|---|---|
output | output(path: PathLike): this | The file to write the signed artifact to; its directory is created when missing, and an existing file is replaced. Required. |
output_? | output_?: string | Where to write the signed artifact (set by output). |
class SignPathGitHubOriginSettings Where the artifact to sign lives in GitHub Actions. Only the artifact id is required: the run, repository and token default to the workflow's own environment (GITHUB_RUN_ID, GITHUB_REPOSITORY, GITHUB_TOKEN).
await SignPathTasks.submit((s) =>
s.projectSlug("zuke").signingPolicySlug("release-signing")
.gitHubOrigin((g) => g.artifactId(artifactId).token(githubToken))
.output("dist/signed/zuke.zip")
);| Member | Signature | Doc |
|---|---|---|
artifactId | artifactId(id: string | number): this | The id of the artifact actions/upload-artifact (v4 or later) uploaded — its artifact-id step output. Required. |
runId | runId(id: string | number): this | The workflow run the artifact belongs to; defaults to GITHUB_RUN_ID. |
repository | repository(name: string): this | The repository, owner/name; defaults to GITHUB_REPOSITORY. |
token | token(value: string): this | The GitHub token the connector reads the run and downloads the artifact with — the workflow's GITHUB_TOKEN (with actions: read and contents: read) is enough. Defaults to the GITHUB_TOKEN environment variable. It is masked out of every message, like the API token. The workflow's own token stops working when the workflow ends, so the task must wait for SignPath to fetch the artifact; submit always waits. |
connectorUrl | connectorUrl(url: string): this | The SignPath GitHub connector to submit through; SignPath's hosted GitHub.com one by default. Only a self-hosted GitHub Enterprise Server needs another. It carries the API token, so it must be https:. |
artifactId_? | artifactId_?: string | The workflow artifact's id (set by artifactId). |
runId_? | runId_?: string | The workflow run's id (set by runId); else GITHUB_RUN_ID. |
repository_? | repository_?: string | The repository, owner/name (set by repository); else GITHUB_REPOSITORY. |
token_? | token_?: string | The GitHub token (set by token); else GITHUB_TOKEN. |
connectorUrl_ | connectorUrl_: string | The connector's URL (set by connectorUrl). |
class SignPathRequestError extends Error A SignPath call produced no usable answer: it could not be sent, it timed out, it was redirected somewhere it must not follow, the download was larger than allowed, or the answer was not shaped as SignPath documents it.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
method | method: string | The HTTP method of the failing call. |
path | path: string | The endpoint path, such as /v1/<org>/SigningRequests (never the host). |
interface SignPathSignedArtifact A signed artifact written to disk by SignPathTasks.download or submit.
| Member | Signature | Doc |
|---|---|---|
path | path: string | The path it was written to — the one configured with .output(...). |
bytes | bytes: number | Its size in bytes. |
class SignPathSigningError extends Error A signing request reached a final status other than Completed — it was Denied by an approver, Failed (a malware scan, a processing error, an artifact SignPath could not retrieve) or Canceled.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
signingRequestId | signingRequestId: string | The signing request's id. |
status | status: string | The final status, such as Denied. |
workflowStatus | workflowStatus: string | undefined | The finer-grained workflow status, such as MalwareDetected, when sent. |
webUrl | webUrl: string | The signing request's page in the SignPath web app. |
interface SignPathSigningRequestStatus Where a signing request stands, from GET …/SigningRequests/<id>/Status.
SignPath documents these status values: InProgress, WaitingForApproval, Completed, Failed, Denied and Canceled — the last four final. workflowStatus is finer-grained (Submitted, RetrievingArtifact, ScanningForMalware, Processing, MalwareDetected, ArtifactRetrievalFailed, …). Both are kept as the strings SignPath sent, so a value added later still reads.
| Member | Signature | Doc |
|---|---|---|
signingRequestId | signingRequestId: string | The signing request's id, a GUID. |
status | status: string | The status, such as InProgress or Completed. |
workflowStatus | workflowStatus: string | undefined | The workflow status, such as ScanningForMalware, when sent. |
isFinalStatus | isFinalStatus: boolean | Whether the request will not change any more: SignPath's isFinalStatus, or — should an answer leave it out — whether status is one of the four documented final ones. |
webUrl | webUrl: string | The signing request's page in the SignPath web app. |
class SignPathStatusSettings extends SignPathConnectionSettings Settings for SignPathTasks.status: the connection and the request's id.
| Member | Signature | Doc |
|---|---|---|
signingRequestId | signingRequestId(id: string): this | The signing request to read — the GUID submit returned. Required. |
signingRequestId_? | signingRequestId_?: string | The signing request's id (set by signingRequestId). |
interface SignPathSubmission What SignPathTasks.submit did: the request, its outcome, the download.
| Member | Signature | Doc |
|---|---|---|
signingRequestId | signingRequestId: string | The new signing request's id, a GUID. |
webUrl | webUrl: string | The signing request's page in the SignPath web app. |
status | status: SignPathSigningRequestStatus | The final status — always Completed; anything else throws. |
signedArtifact | signedArtifact: SignPathSignedArtifact | undefined | The signed artifact, when .output(...) asked for it to be downloaded. |
class SignPathSubmitSettings extends SignPathConnectionSettings Settings for SignPathTasks.submit.
| Member | Signature | Doc |
|---|---|---|
projectSlug | projectSlug(slug: string): this | The SignPath project's slug. Required. |
signingPolicySlug | signingPolicySlug(slug: string): this | The signing policy's slug, such as release-signing — it decides the certificate and whether an approver must sign off. Required. |
artifactConfigurationSlug | artifactConfigurationSlug(slug: string): this | The artifact configuration's slug — which files inside the artifact are signed, and how. The project's default configuration when unset. |
description | description(text: string): this | A description shown on the request, such as the version being released. |
parameter | parameter(name: string, value: string): this | A user-defined parameter the artifact configuration reads (a version number to stamp, say). Names are letters, digits, ., - and _. Repeatable. |
artifact | artifact(path: PathLike): this | Upload this local file as the artifact (SubmitWithArtifact). Exactly one of artifact and gitHubOrigin is required. SignPath's open-source programme accepts only builds whose origin it can verify, so for it use gitHubOrigin instead. |
gitHubOrigin | gitHubOrigin(configure: Configure<SignPathGitHubOriginSettings>): this | Have SignPath fetch the artifact from a GitHub Actions workflow run and verify its origin. Exactly one of artifact and gitHubOrigin is required. |
output | output(path: PathLike): this | Download the signed artifact to this file once the request completes; its directory is created when missing. Without it, submit only waits. |
timeout | timeout(duration: string | number): this | Fail with a SignPathTimeoutError when the request is not final after this long ("30m", or ms); default ten minutes. A policy that needs a manual approval wants longer. |
pollInterval | pollInterval(duration: string | number): this | Poll the request's status this often ("5s", or ms; at least one second); default ten seconds. |
projectSlug_? | projectSlug_?: string | The project's slug (set by projectSlug). |
signingPolicySlug_? | signingPolicySlug_?: string | The signing policy's slug (set by signingPolicySlug). |
artifactConfigurationSlug_? | artifactConfigurationSlug_?: string | The artifact configuration's slug (set by artifactConfigurationSlug). |
description_? | description_?: string | The request's description (set by description). |
parameters_ | parameters_: Array<unknown> | User-defined parameters, in the order set (set by parameter). |
artifact_? | artifact_?: string | A local file to upload (set by artifact). |
gitHubOrigin_? | gitHubOrigin_?: SignPathGitHubOriginSettings | A GitHub Actions artifact to fetch (set by gitHubOrigin). |
output_? | output_?: string | Where to write the signed artifact (set by output). |
timeout_ | timeout_: number | How long to wait for the request to finish, in ms (set by timeout). |
pollInterval_ | pollInterval_: number | How often to poll its status, in ms (set by pollInterval). |
const SignPathTasks: SignPathTasksApi Typed task functions for SignPath's code-signing service.
interface SignPathTasksApi The operations SignPathTasks exposes.
| Member | Signature | Doc |
|---|---|---|
submit | submit(configure: Configure<SignPathSubmitSettings>): Promise<SignPathSubmission> | Submit a signing request, wait for it to complete, and — with .output(path) — download the signed artifact. ``ts const { signingRequestId } = await SignPathTasks.submit((s) => s.organizationId(orgId).apiToken(token) .projectSlug("zuke").signingPolicySlug("release-signing") .gitHubOrigin((g) => g.artifactId(artifactId)) .timeout("30m").output("dist/signed/zuke.zip") ); `` |
status | status(configure: Configure<SignPathStatusSettings>): Promise<SignPathSigningRequestStatus> | Read a signing request's status once — no waiting. ``ts const { status } = await SignPathTasks.status((s) => s.signingRequestId(id) ); `` |
download | download(configure: Configure<SignPathDownloadSettings>): Promise<SignPathSignedArtifact> | Download a completed signing request's signed artifact. ``ts await SignPathTasks.download((s) => s.signingRequestId(id).output("dist/signed/zuke.exe") ); `` |
class SignPathTimeoutError extends Error A signing request was still not final when the wait ran out — typically one waiting for a manual approval. The request is left as it is; approve it and fetch the result later with SignPathTasks.download.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
signingRequestId | signingRequestId: string | The signing request's id. |
status | status: string | The last status seen, such as WaitingForApproval. |
timeoutMs | timeoutMs: number | How long the wait lasted, in milliseconds. |
webUrl | webUrl: string | The signing request's page in the SignPath web app. |
class SignPathValidationError extends Error The SignPath GitHub connector refused to create the signing request because the build's origin did not verify — the artifact is not from this workflow run, a job ran on a self-hosted runner, the GitHub token cannot read the run, and the like. issues lists what the connector reported.
| Member | Signature | Doc |
|---|---|---|
name | name: string | The error name. |
issues | issues: unknown | The validation errors, in the order the connector reported them. |
interface SignPathValidationIssue One build-system validation error the GitHub connector reported.
| Member | Signature | Doc |
|---|---|---|
error | error: string | What failed, as the connector worded it (scrubbed and capped). |
howToFix | howToFix: string | undefined | The connector's advice on fixing it, when it gave any. |
type SigntoolHashAlgorithm = SHA1 | SHA256 The digest algorithms /hash accepts.
const SigntoolTasks: SigntoolTasksApi Typed task functions for the Windows SDK's signtool.
interface SigntoolTasksApi The shape of SigntoolTasks.
| Member | Signature | Doc |
|---|---|---|
verify | verify(configure: Configure<SigntoolVerifySettings>): Promise<CommandOutput> | Check a file's Authenticode signature with signtool verify; a file that is unsigned or fails the policy exits non-zero, which throws unless .noThrow() is set. |
class SigntoolVerifySettings extends ToolSettings Settings for signtool verify.
| Member | Signature | Doc |
|---|---|---|
authenticodePolicy | authenticodePolicy(): this | Verify against the default Authenticode policy (/pa) — what a signed .exe or .msi needs. Without it signtool applies the Windows driver policy and rejects ordinary code-signing certificates. |
kernelPolicy | kernelPolicy(): this | Verify against the kernel-mode driver signing policy (/kp). |
policy | policy(guid: string): this | Verify against the policy with this GUID (/pg). |
allSignatures | allSignatures(): this | Verify every signature of a file that carries several (/all). |
signatureIndex | signatureIndex(index: number): this | Verify only the signature at this index, from 0 (/ds). |
hash | hash(algorithm: SigntoolHashAlgorithm): this | Look the file up in a catalog only for this digest algorithm (/hash). |
catalog | catalog(path: PathLike): this | Verify against this catalog file (/c). |
rootSubjectName | rootSubjectName(name: string): this | Require the signing certificate to chain to a root whose subject name contains this text (/r). |
timestampWarning | timestampWarning(): this | Warn when the signature is not timestamped (/tw). |
description | description(): this | Print the signature's description and URL (/d). |
verbose | verbose(): this | Print the signer, the chain and the timestamp (/v). |
debug | debug(): this | Print debugging information (/debug). |
file | file(...paths: PathLike[]): this | The files to verify (positional), at least one. A relative path that starts with - is passed as ./-… so it cannot be read as an option; one that starts with / is refused, since signtool would read it as an option and on Windows it means the current drive's root — give a drive-qualified or a relative path instead. |