@kovari/kciep
v1.0.1
Published
Deterministic KCBF extension packaging, Ed25519 signing, SHA-256 verification and CLI tooling for Kovari Code IDE.
Downloads
69
Maintainers
Readme
Version badge policy: the
version-v1.0.1badge above records the version in this repository (1.0.1). It intentionally does not mirror the npmlatesttag, which may continue to point at the previous published release until this version is published.
Release line:
@kovari/[email protected]· Configuration peer tool:[email protected]· Module format: ESM
KCIep is the packaging and verification component for Kovari Code IDE (Kovari Code IDE Extension Packager). It collects an extension project, writes a ZIP-compatible KCBF v1 archive, creates per-file SHA-256 integrity metadata, optionally signs the archive metadata with Ed25519, and verifies the result against the configured trust policy.
KCBF and VSIX are analogous in purpose, not interchangeable formats. KCIep follows a workflow similar to vsce—inspect an extension project, package it, and validate the artifact—but .kcbf is Kovari's own format. It is not a native VS Code .vsix and cannot be installed using code --install-extension. See KCBF vs VSIX before designing an installation flow.
Contents
- Capabilities
- How KCIep relates to vsce
- Requirements and installation
- Quick start
- Extension project layout
- KCBF manifest
- CLI reference
- Signing and verification
- Trust policy
- KCBF file format
- JavaScript and TypeScript API
- Configuration and limits
- npm scripts and quality checks
- Testing guide
- Security model and limitations
- Troubleshooting
- Release checklist
- Repository structure
- Related documentation
- Licence
Capabilities
| Capability | What KCIep does | Main implementation |
|---|---|---|
| Deterministic packaging | Sorts included paths and fixes archive timestamps when deterministic is enabled | createKCBF() |
| File discovery | Lists candidate project files while skipping build output, dependency folders and common secrets | listFiles() |
| SHA-256 integrity | Stores file sizes and hashes and computes an aggregate content digest | kcbf/integrity.json |
| Optional Ed25519 signatures | Signs canonical JSON for the manifest and integrity records | Node.js crypto |
| Signature fingerprint | Prints SHA-256 over the signing key's DER SubjectPublicKeyInfo bytes | keyFingerprint |
| Verification | Checks paths, ZIP entries, manifest consistency, file hashes, signature and trust policy | verifyKCBF() |
| Resource limits | Enforces maximum entry count, per-file bytes and total uncompressed bytes | kciem-config settings |
| Command-line workflow | Inspect config, list files, package and verify from a terminal | kciep CLI |
| Machine-readable schemas | Generates JSON Schema for KCBF manifest, integrity and signature records | out/schema/ |
KCIep creates package artifacts; it does not itself implement the Kovari Code IDE extension host, install or execute packaged extensions, publish extensions to a marketplace, or provide an interactive trust-consent UI. The host application remains responsible for installation policy and runtime isolation.
How KCIep relates to vsce
Microsoft's @vscode/vsce tool packages VS Code extensions into .vsix files. VS Code's documented workflow is vsce package, followed by installing the resulting .vsix through the Extensions UI or code --install-extension.
KCIep fills a comparable packaging role for Kovari Code IDE:
| Topic | vsce / VSIX | kciep / KCBF |
|---|---|---|
| Primary host | Visual Studio Code and compatible VS Code builds | Kovari Code IDE host integrations that understand KCBF |
| Artifact extension | .vsix | .kcbf |
| Container | VS Code's documented VSIX package structure | ZIP-compatible KCBF v1 structure defined in this repository |
| Main manifest | VSIX/VS Code extension manifest conventions | kcbf/manifest.json plus extension/kcbf.extension.json payload file |
| Integrity | Defined by VS Code/VSIX tooling and its ecosystem | Explicit per-file SHA-256 list plus aggregate digest |
| Signature and trust | Follow VS Code Marketplace/tooling policy for the chosen distribution path | Optional Ed25519 signature plus local Kciem trust policy |
| Install command | code --install-extension example.vsix | Host-specific KCBF installation API/flow must be implemented separately |
KCBF vs VSIX compatibility
A .kcbf file is a package for Kovari Code IDE, not a renamed VSIX. Although both can serve as extension-distribution artifacts, VS Code will not recognize KCBF merely because the container is ZIP-compatible. A VSIX needs the expected VS Code package structure and metadata; KCIep emits the KCBF manifests and integrity/signature records described below.
If your project must support both hosts, maintain two explicit release targets:
- Run
vsce package(from@vscode/vsce) to create the native VS Code.vsix. - Run KCIep to create and verify the Kovari
.kcbf.
Do not rename one artifact to the other extension and do not assume that one package contains the metadata or policy expected by the other host. Reference: VS Code — Publishing and packaging extensions.
Requirements and installation
| Requirement | Version / details |
|---|---|
| Node.js | >=22.12 |
| npm | >=10 |
| Module format | ESM ("type": "module") |
| TypeScript | Strict TypeScript sources; published declarations are in dist/ |
| Runtime dependencies | commander, [email protected], yauzl, yazl, zod |
| Package licence | MIT |
Install the package in the extension project or tooling workspace:
npm install @kovari/kciepFor a global CLI installation:
npm install --global @kovari/kciep
kciep --helpFor a one-off invocation without a global install:
npx --yes @kovari/kciep --helpInstall only from a registry or source you trust. Review the package's release and licence before embedding it in an application.
Quick start
1. Create the extension manifest
Create kcbf.extension.json in the extension project root:
{
"schemaVersion": 1,
"name": "hello-world",
"displayName": "Hello World",
"publisher": "kovari",
"version": "1.0.0",
"description": "A sample Kovari Code IDE extension.",
"engines": {
"kovariCode": ">=1.0.0"
},
"main": "extension.js"
}Required fields are schemaVersion (exactly 1), name, displayName, publisher, version, description, and engines.kovariCode. The name value must match ^[A-Za-z0-9][A-Za-z0-9._-]*$. main is optional in the current manifest schema. Keep the host-specific manifest distinct from package.json; they serve different consumers.
2. Add a Kciem configuration
Create kciem.config.json beside kcbf.extension.json:
{
"schemaVersion": 1,
"target": "universal",
"deterministic": true,
"limits": {
"maxFileBytes": 26214400,
"maxTotalBytes": 262144000,
"maxEntries": 10000
},
"signing": {
"required": false,
"algorithm": "Ed25519"
},
"trust": {
"mode": "prompt",
"trustedPublishers": ["kovari"],
"trustedKeys": [],
"revokedKeys": []
},
"provider": {
"timeoutMs": 30000,
"retries": 2
}
}The example allow-lists kovari for an unsigned package. In prompt mode, unknown publishers or unpinned signing keys are denied by the trust function and marked as requiring confirmation; KCIep does not display a confirmation dialog itself. See kciem-config docs when using the source monorepo, or the kciem-config package when consuming the published package.
3. Inspect the project, then package it
# Show the effective, validated config
npx kciep config --cwd .
# List candidate files before packaging
npx kciep list --cwd .
# Create a deterministic KCBF using the project's configuration
npx kciep package --cwd . --out ./out/hello-world-1.0.0.kcbf
# Verify archive structure, integrity, signature (when present) and trust policy
npx kciep verify ./out/hello-world-1.0.0.kcbf --cwd .KCIep outputs structured JSON for package and verification results. A failed verification sets a non-zero process exit code; treat any valid: false result as a release failure.
Extension project layout
A minimal project might look like this:
hello-world/
├── kcbf.extension.json # KCIep's required extension identity manifest
├── kciem.config.json # Validated build limits and trust policy
├── extension.js # Example entry point referenced by the manifest
├── package.json # Optional project/dependency metadata
├── README.md # Optional project docs
├── src/ # Your source code, if applicable
│ └── index.js
└── out/ # Generated output; skipped by file enumerationFiles included and skipped
listFiles(cwd) recursively enumerates regular files, sorts paths, and skips directories named node_modules, .git, and out. It excludes paths ending in .kcbf or .tgz, and excludes common secret filenames such as .env, .env.*, id_rsa, id_ed25519, and files ending in .pem, .key, .p12, or .pfx. Symbolic links are not followed.
This is a defence-in-depth convenience, not a substitute for reviewing the file list. Secrets may be stored under unrecognised names. Run kciep list before publishing, maintain a precise .gitignore, and use a clean staging directory for official releases.
KCBF manifest
The archive's kcbf/manifest.json contains:
| Field | Type | Description |
|---|---|---|
| kcbfVersion | literal 1 | KCBF container-schema generation |
| id | string | ${publisher}.${name} |
| publisher, name, version | strings | Identity copied from kcbf.extension.json |
| target | supported target enum | Target from validated kciem.config.json |
| createdAt | ISO-like string | Fixed timestamp when deterministic mode is enabled; real timestamp otherwise |
| fileCount | integer | Count of payload files represented in integrity metadata |
| totalBytes | integer | Total payload bytes before ZIP compression |
| contentDigest | 64-character lowercase hex | SHA-256 digest of canonical path/size/file-hash records |
A manifest example is documented in docs/KCBF.md. Generated JSON Schema files live in out/schema/ after npm run build.
CLI reference
kciep --help and kciep --version
Commander provides the help and version output:
kciep --help
kciep --versionkciep config
Load kciem.config.json from a project directory, apply supported environment overrides and print the effective config:
kciep config --cwd ./examples/hello-worldkciep list
Print sorted candidate payload file paths; it does not write a package:
kciep list --cwd ./examples/hello-worldkciep package
Options currently supported:
| Option | Description |
|---|---|
| -C, --cwd <dir> | Extension project directory (defaults to the current directory) |
| -o, --out <file> | Output path for the .kcbf artifact |
| --sign-key <file> | Ed25519 private-key PEM file used to sign metadata |
| --key-id <id> | Explicit signing key ID; overrides signing.keyId |
Unsigned deterministic package:
kciep package --cwd ./extension --out ./release/extension.kcbfSigned package (the private key is read from disk and is not bundled as a payload file by the signing option):
kciep package \
--cwd ./extension \
--out ./release/extension-signed.kcbf \
--sign-key ./keys/release-private.pem \
--key-id kovari-release-2026The command prints the signer fingerprint. Pin that fingerprint in the verification config before using strict trust verification. Do not store private keys in the extension project being packaged; keep signing keys outside the project or in a dedicated secret store, restrict access, and back them up securely.
kciep verify <file>
Verify the archive using configuration from the supplied --cwd:
kciep verify ./release/extension-signed.kcbf --cwd ./verification-policyA zero exit status means the implementation found no structural, integrity, signature or trust errors under the supplied policy. It does not prove that the extension is benign, compatible with the host, or safe to execute.
Signing and verification
KCIep supports optional Ed25519 signing through Node.js's built-in node:crypto API. It signs canonical JSON containing the manifest and integrity document. The public key is embedded in kcbf/signature.json; a trusted key must still be identified by a fingerprint pinned in local configuration.
Generate a test signing key
Run this in a private directory outside the extension project, using a trusted Node.js installation:
// save as generate-key.mjs and run: node generate-key.mjs
import { generateKeyPairSync, createHash, createPublicKey } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
await mkdir("./private-keys", { recursive: true, mode: 0o700 });
const { privateKey, publicKey } = generateKeyPairSync("ed25519", {
privateKeyEncoding: { type: "pkcs8", format: "pem" },
publicKeyEncoding: { type: "spki", format: "pem" }
});
await writeFile("./private-keys/release-private.pem", privateKey, { mode: 0o600 });
await writeFile("./private-keys/release-public.pem", publicKey, { mode: 0o644 });
// Node export gives the canonical DER SubjectPublicKeyInfo bytes used by KCIep.
const fingerprint = createHash("sha256")
.update(createPublicKey(publicKey).export({ type: "spki", format: "der" }))
.digest("hex");
console.log(`Pin this fingerprint in the trustedKeys array: ${fingerprint}`);Key hygiene: this example is for local development. Add key directories to
.gitignore, never commit production private keys, and prefer your CI secret store or hardware-backed key management for releases. The tool does not provide a key vault.
Once the fingerprint is known, set trust.trustedKeys in the verification policy:
{
"trust": {
"mode": "strict",
"trustedPublishers": [],
"trustedKeys": ["REPLACE_WITH_64_HEX_CHARACTER_SHA256_FINGERPRINT"],
"revokedKeys": []
}
}This is a partial configuration object: defineConfig()/loadConfig() apply the other defaults. Replace the placeholder before running verification; it is not a valid fingerprint as shown.
Signature does not equal trust
A mathematical signature-valid result proves that the included signature matches the supplied public key and signed metadata. It does not establish who owns that key. The configured trust policy separately decides whether that key is accepted. Always check both signatureValid and trusted, and then check the overall valid field.
Trust policy
The trust decision comes from kciem-config.evaluateTrust():
| Condition | Allowed? | Confirmation flag |
|---|---:|---:|
| Key ID is revoked | No | No; revocation takes precedence |
| Public-key SHA-256 fingerprint is pinned | Yes | No |
| permissive mode and key is not revoked | Yes | No |
| Unsigned publisher is allow-listed | Yes | No |
| Unknown package in prompt mode | No | Yes |
| Unknown package in strict mode | No | No |
The library returns a decision only; it does not display UI. A host using prompt mode must obtain user confirmation itself, then make an explicit policy decision in its own install workflow. Do not mistake requiresConfirmation: true for approval.
KCBF file format
KCBF v1 is a ZIP-compatible archive with a custom, versioned internal layout:
example.kcbf
├── kcbf/
│ ├── manifest.json # Package identity and summary
│ ├── integrity.json # SHA-256 for each payload file
│ └── signature.json # Optional Ed25519 key/signature record
└── extension/
├── kcbf.extension.json # Host manifest included in payload
├── extension.js
└── ... # Other regular payload filessignature.json is omitted when the package is unsigned. integrity.json lists the payload paths under extension/, each file's byte length and SHA-256 hash, and the aggregate content digest. The archive's ZIP container itself is not described by the manifest JSON Schema; the individual records have separate schemas under out/schema/.
Detailed record semantics and forward-compatibility considerations are in docs/KCBF.md.
JavaScript and TypeScript API
The package is ESM-first. The public root module exports createKCBF, verifyKCBF, listFiles, plus defineConfig, loadConfig and evaluateTrust re-exported from kciem-config. Type declarations are published with the package.
List candidate files
import { listFiles } from "@kovari/kciep";
const paths = await listFiles("./my-extension");
console.log(paths);Create and verify a package
import { createKCBF, verifyKCBF, loadConfig } from "@kovari/kciep";
const cwd = "./my-extension";
const config = await loadConfig({ cwd });
const result = await createKCBF({
cwd,
packagePath: "./release/my-extension.kcbf",
config
});
console.log(result.packagePath, result.manifest.contentDigest);
const verified = await verifyKCBF(result.packagePath, config);
if (!verified.valid) {
throw new Error(`KCBF verification failed: ${verified.errors.join("; ")}`);
}Function summary
| API | Purpose |
|---|---|
| listFiles(cwd?) | Returns sorted relative file paths eligible for packaging |
| createKCBF(options?) | Creates KCBF v1; optional PEM key signs canonical package metadata |
| verifyKCBF(path, config?) | Verifies archive structure, manifests, integrity, signature and policy |
| defineConfig(value?) | Validate an in-memory config and apply defaults |
| loadConfig(options?) | Load a config file and apply environment overrides |
| evaluateTrust(config, publisher, keyId?, keyFingerprint?) | Evaluate the configured trust policy |
createKCBF options: cwd, packagePath, config, privateKey (PEM contents, not a file path), and keyId. The CLI reads --sign-key from a file and passes its contents into this API.
verifyKCBF results contain valid, signatureValid (undefined for unsigned artifacts), trusted, trustReason, optional keyFingerprint, errors, and the parsed manifest. Callers should handle thrown errors too: a malformed ZIP or unparseable required JSON record can throw before a result is returned.
Configuration and limits
The default config filename is kciem.config.json. Supported environment overrides are:
| Variable | Overrides | Notes |
|---|---|---|
| KCIEM_TARGET | target | Must be one of the declared target strings |
| KCIEM_TRUST_MODE | trust.mode | strict, prompt or permissive |
Defaults are target: "universal", deterministic packaging enabled, maxFileBytes: 25 MiB, maxTotalBytes: 250 MiB, maxEntries: 10000, optional Ed25519 signing, prompt trust mode, 30-second provider timeout and two retries. These limits bound uncompressed archive reads; adjust them for your product through validated config rather than bypassing validation.
See the kciem-config README and the local https://github.com/LoonCodeHunter/kciem-config/blob/main/docs/CONFIGURATION.md when working with both repositories together.
npm scripts and quality checks
Run commands from the package root. npm ci requires access to the registry for any dependencies not already cached. The project targets Node.js >=22.12 and npm >=10.
| Command | Purpose |
|---|---|
| npm run | List all scripts defined in package.json |
| npm run clean | Remove generated dist/ and out/ folders using Node APIs |
| npm run typecheck | Run strict TypeScript checking without emit |
| npm run build | Clean, compile TypeScript, generate CLI shim and KCBF JSON schemas |
| npm run test:unit | Run Node's built-in test runner against existing build output |
| npm test | Build first, then run unit tests |
| npm run validate | Check expected build artifacts and core metadata |
| npm run check:metadata | Check package/lock versions, script declarations, export targets and schemas |
| npm run check | Run typecheck, tests, validation and metadata checks |
| npm run pack:dry | Preview npm package contents without executing pack lifecycle scripts |
| npm run publish:dry | Simulate a public npm publish without changing the registry |
| npm run release:check | Run all checks and both packaging dry runs |
| npm run security | Run npm audit --audit-level=high; requires npm registry access |
Clean-clone verification sequence
# Recommended supported runtime: Node 22.12+ or Node 24, npm 10+
node --version
npm --version
npm ci
npm run release:checkTo inspect publish contents separately:
npm pack --dry-run --json --ignore-scriptsThe pack:dry and publish:dry scripts intentionally use --ignore-scripts; this prevents npm lifecycle recursion because prepack itself runs npm run check. Always run npm run release:check separately first so typechecking, build, tests and metadata assertions actually execute. npm documents the distinction between npm pack and npm run pack, and defines when lifecycle hooks run: npm scripts and npm pack.
Testing guide
The Node test suite exercises package behaviour without needing a running Kovari Code IDE instance. Current test areas include:
- deterministic package output and sorted file discovery;
- omission of common secret files and generated archive artifacts;
- creating/verifying unsigned and signed packages;
- checking pinned signing-key fingerprints and the required-signature policy;
- detecting tampered file bytes and inconsistent manifest summaries;
- rejecting traversal-style and duplicate archive entry paths;
- validating config defaults, nested settings, strict unknown-property handling, environment overrides and trust decisions.
Use npm test as the normal local path because it builds dist/ before running tests. npm run test:unit expects built files to already exist. CI is defined in .github/workflows/ci.yml and covers Node.js 22 and 24.
Tests increase confidence in these scenarios; they do not prove that arbitrary extension code is safe, that every host integration is compatible, or that every possible malformed archive is rejected. Add host integration tests before shipping an installer.
Security model and limitations
- Inspect before packaging.
listFiles()excludes known secret patterns but cannot infer every sensitive file name. - Use signatures for publisher verification. An embedded public key alone is not trusted identity; pin a known fingerprint.
- Keep private keys out of payload roots. The scanner excludes common PEM/key names, but signing keys should be stored outside the project regardless.
- Treat archives as untrusted.
verifyKCBF()limits entry count and uncompressed size, rejects unsafe paths and symlinks, verifies hashes and validates trust policy. Still update dependencies and test against hostile inputs. - Verification is not sandboxing. A valid package can still contain harmful code. The host must apply permissions, sandboxing, capability restrictions and runtime policy.
- No implicit prompt UI.
promptmode returns a denial plusrequiresConfirmation; the host must ask the user. - No secret upload. KCIep does not upload packages or keys to a remote service.
Report vulnerabilities privately to the maintainers listed in the repository's security policy instead of putting working exploit details in public issues.
Troubleshooting
Missing kcbf.extension.json
Run the command with --cwd set to the extension root. The extension manifest is required at that root; it is not inferred from package.json.
Package would exceed the maximum entry count or size limit
Review limits in kciem.config.json and run kciep list --cwd . to inspect the input set. Increase limits only when appropriate for your application; do not disable safety controls without a threat-model review.
The current policy requires an Ed25519 signing key
signing.required is true, so add --sign-key ./path/to/private.pem (or supply the privateKey PEM contents to the API). Do not commit that key.
Signing key fingerprint is not trusted
Add the expected SHA-256 fingerprint to trust.trustedKeys in the verification config, or obtain confirmation via your host's explicit prompt workflow. Do not trust a fingerprint merely because it appears in the archive that is being verified.
Invalid Ed25519 signature or integrity mismatch
The archive may be damaged, modified or generated using an incompatible implementation. Rebuild from a clean tree, verify the signing-key pair and compare the full error list. Do not rename the artifact to .vsix as a workaround.
The file cannot be installed by VS Code
That is expected for .kcbf; VS Code's native command installs .vsix packages. Implement a KCBF-aware installer in the Kovari Code IDE host, or separately build a VSIX with vsce if distributing to VS Code.
Release checklist
- [ ] Review
git diffand verify no private keys, credentials,.envfiles or generated package tarballs are staged. - [ ] Confirm
package.jsonandpackage-lock.jsonboth say@kovari/[email protected]and lock[email protected]. - [ ] Run
npm cion Node 22.12+ / npm 10+. - [ ] Run
npm run release:checkand review the complete logs. - [ ] Review
npm pack --dry-run --json --ignore-scriptsoutput. - [ ] Create a sample
.kcbffrom a clean project and verify it with the intended trust configuration. - [ ] If signing, verify with a fingerprint pinned through a trusted channel; never pin a key solely because it arrived in the package.
- [ ] Test the installer and permission boundaries in Kovari Code IDE.
- [ ] Publish only after checking that
1.0.0has not already been published: an npm package name/version pair cannot normally be reused after publication.
Repository structure
kciep/
├── assets/icon.png
├── docs/
│ ├── KCBF.md
│ ├── VSCE-COMPATIBILITY.md
│ └── RELEASE-CHECKLIST.md
├── out/ # Generated CLI shim and JSON Schemas
├── src/
│ ├── cli.ts
│ └── index.ts
├── tests/
│ ├── fixture/
│ ├── helpers/zip-tools.mjs
│ └── kciep.test.mjs
├── scripts/ # Clean, build, validation, metadata checks
├── dist/ # Compiled JS and declarations
├── package.json
├── package-lock.json
├── tsconfig.json
└── tsconfig.build.jsonRelated documentation
- KCBF format reference
- KCBF vs VSIX compatibility and migration notes
- Release checklist
kciem-configon npm- VS Code extension packaging and publishing
- npm scripts
- npm pack
Production integration guides
- IDE release integration — a CI pipeline from source validation to verified KCBF output.
- KCBF implementation checklist — checks the host should perform before accepting an artifact.
- KCBF format · VSIX compatibility · Release checklist
Use in Kovari Code IDE
KCIep can package extension projects, but a production host must still verify the resulting archive before install, request user consent where required, extract safely and run extensions with least privilege. KCBF and VSIX are related by workflow, not by binary compatibility.
Licence
MIT. See LICENSE.
Kovari Code IDE ecosystem: www.kovaricode.com is planned; the website link will be activated after the domain and site are live.
