Skip to main content

Installer and license manager for the skilldecks-* family of Claude Code skills

Project description

skilldecks-installer

Installer and license manager for the skilldecks-* family of Claude Code skills.

What's in this pack → INDEX.md · Skills · Agents · Workflows · Scripts

What this does

skilldecks-installer is the bridge between a customer's purchased licenses and a working installation of skilldecks skills in their Claude Code environment. It:

  1. Reads per-purchase license files from ~/.skilldecks/licenses/
  2. Computes the merged entitlement (union by SKU)
  3. Installs each owned SKU as a Python package via pip
  4. Copies each SKU's SKILL.md + references into ~/.claude/skills/skilldecks/<sku>/ so Claude Code discovers them

It is also the home of the skilldecks CLI (install, update, add-license, status, setup, auth, init, doctor).

skilldecks update enforces the per-license updates_until field, and skilldecks status shows days-until-expiry per SKU. Lapsed-window SKUs skip the wheel fetch with a friendly renewal nudge; the installed version keeps working. See docs/plans/2026-05-10-perpetual-license-launch.md §6.1 to §6.3 for the full runtime contract, the 7-day signed-URL footgun, and the manual renewal flow.

Status

Alpha. Phase 1 of the marketplace launch plan (docs/plans/2026-05-09-marketplace-launch.md). R2 URL signing wired 2026-05-09 (see "Operator setup — R2 signing" below).

Operator setup — R2 signing

skd-admin license issue --method wheel signs R2 URLs at issuance time so the customer's skilldecks install can pip install the wheel directly. Only the operator needs R2 credentials — the signed URL embedded in the license YAML is self-authenticating until expiry.

One-time install

pip install 'skilldecks-installer[r2]'   # adds boto3

Required environment variables

Set these in your shell rc (~/.zshrc / ~/.bash_profile) so skd-admin can read them. They never end up in git, license YAMLs, or customer artifacts.

Var Example Where to find it
SKILLDECKS_R2_ACCOUNT_ID 2f6b6043df01b12524c5752df11ffb22 Cloudflare Dashboard → R2 → bucket details (or the hex prefix in the bucket URL)
SKILLDECKS_R2_BUCKET skilldecks The bucket name
SKILLDECKS_R2_ENDPOINT https://2f6b6043df01b12524c5752df11ffb22.r2.cloudflarestorage.com The S3 API endpoint URL without the bucket suffix
SKILLDECKS_R2_ACCESS_KEY (redacted) Cloudflare Dashboard → R2 → Manage API Tokens → create with "Object Read & Write" scoped to the bucket
SKILLDECKS_R2_SECRET_KEY (redacted) Same step as above. Cloudflare shows the secret once at creation — save to a password manager immediately.

How it behaves

Scenario Result
All 5 env vars set, URL bucket matches SKILLDECKS_R2_BUCKET URL signed with X-Amz-Signature=… query string, valid 7 days (AWS Sig v4 max)
Any env var missing/empty Unsigned URL emitted + WARNING printed to stderr. License YAML is still issuable for dev/testing; install will fail at the actual download step.
URL bucket prefix doesn't match SKILLDECKS_R2_BUCKET R2SigningError — operator misconfigured something. Update skus.yaml URL templates or the env var.

Customer perspective

Customers don't install [r2] and don't set any env vars. Their license YAML contains a pre-signed URL; skilldecks install runs pip install <url> and that's it.

URL expiry

Signed URLs are valid for 7 days (the AWS Sig v4 maximum for static credentials). If a customer takes longer than 7 days to install after receiving a license, run skd-admin license issue again for the same SKU and re-email the YAML.

Customer install (eventually)

curl -L https://skilldecks.<tld>/install | sh

This script will install skilldecks-installer (currently from a private wheel URL or git+ssh) and run skilldecks setup to drop the Claude Code skill into ~/.claude/skills/skilldecks/.

To opt into voice/sound feedback during install:

curl -L https://skilldecks.<tld>/install | SKILLDECKS_WITH_VOICE=1 sh

To skip the third-party bundles entirely (minimal install):

curl -L https://skilldecks.<tld>/install | SKILLDECKS_SKIP_BUNDLES=1 sh

License redemption

After a paid purchase, the operator (or, eventually, the skilldecks-store backend) issues a signed license YAML and uploads it to R2 under skilldecks/_redeem/<token>/license.yaml. The customer receives a presigned GET URL (AWS Sig v4, 7-day max TTL) by email and redeems it with one command:

skilldecks install --redeem 'https://<account>.r2.cloudflarestorage.com/skillsformarketing/_redeem/<token>/license.yaml?X-Amz-Signature=...'

This:

  1. Downloads the YAML over HTTPS (30s timeout, identifies as User-Agent: skilldecks-installer/<version> so we're greppable in R2 access logs).
  2. Verifies the embedded Ed25519 signature locally against the bundled public keys (dev + prod). The URL alone is NOT trusted — a compromised email or a typo'd URL must not be able to plant arbitrary YAML on the customer's machine.
  3. Writes the file atomically to ~/.skilldecks/licenses/<license_id>.yaml.
  4. Continues with the normal install (same code path as bare skilldecks install).

If anything fails (HTTP error, expired URL, bad signature, a license with the same license_id is already redeemed) the command exits with status 1 and does not run any pip install — redemption is all-or-nothing.

The 7-day TTL is the operator's policy; if a customer's URL has expired, the operator re-issues a fresh one (skd-admin license issue or, eventually, the store-backend re-signs). The redeem flow does NOT extend or refresh URLs on its own.

Security model in one paragraph

The presigned URL is just a delivery mechanism — the trust anchor is the Ed25519 signature inside the YAML, verified against public keys shipped in the wheel under skilldecks_installer/_keys/. As long as the operator's private key stays vaulted (~/.skilldecks/signing/ by convention), any tampering anywhere along the email/network path fails the signature check at redeem time, so the customer cannot end up with an unauthorized license.

What skilldecks setup installs

Component Source Lands at Default
skilldecks CLI + installer skill this package ~/.claude/skills/skilldecks-installer/ + pip user bin always
~/.skilldecks/ scaffold this package ~/.skilldecks/{licenses,credentials,vendor,templates}/ always
Best-practice agents/commands/rules/skills shanraisshan/claude-code-best-practice ~/.claude/{agents,commands,rules,skills}/best-practice/ on, --skip-bundles to opt out
Best-practice CLAUDE.md template same ~/.skilldecks/templates/best-practice-CLAUDE.md (staged, never auto-applied) on
Voice/sound hooks shanraisshan/claude-code-hooks ~/.claude/hooks/skilldecks-voice/ off, --with-voice to enable

Bundles are namespaced under best-practice/ and skilldecks-voice/ subdirs so we never collide with files the user has already authored.

Voice feedback (opt-in)

skilldecks voice enable    # fetch + install hooks, merge bindings into ~/.claude/settings.json
skilldecks voice status    # show enabled/disabled + binding count
skilldecks voice disable   # remove bindings (keeps files cached, fast re-enable)

Bindings are tagged skilldecks-voice:<HookEvent> so disable removes only our entries — your other hooks stay intact.

Activation (online seat enforcement)

A license can grant N concurrent machine activations. The mechanism:

  1. The license YAML carries a seats: N field (default 1; bundles like agency_multi_client ship seats: 5).
  2. On skilldecks install, the CLI POSTs {license_id, fingerprint} to ${SKILLDECKS_ACTIVATION_URL}/activate. The server records the activation if the cap allows, refuses with HTTP 409 if full.
  3. Successful activations are cached at ~/.skilldecks/activation_<license_id>.json for 30 days by default, so subsequent installs on the SAME machine don't re-call. The cache stores ONLY the activation token + seat metadata — never license content. Operators who want a tighter window (e.g. faster seat-revocation propagation) set SKILLDECKS_ACTIVATION_CACHE_DAYS=<N> where N is an integer in [1, 90]. Out-of-range values raise a clear error at ActivationClient() construction time — a typo in a deployment env file fails loud at startup instead of silently leaking seats.
  4. The machine fingerprint is the install_id already used for telemetry (~/.skilldecks/telemetry/.install-id). No new per-machine identifier is introduced; if you opted out of telemetry, the activation flow still uses this anonymous random id but doesn't send any telemetry events.

Backwards-compat

If SKILLDECKS_ACTIVATION_URL is unset, activation is skipped and skilldecks install prints a stderr note: "activation skipped (SKILLDECKS_ACTIVATION_URL not set)". Customer flows keep working until the operator deploys the activation server. Once deployed, the env var flips enforcement on with no client-side code change.

Customer commands

# Start using the license on this machine — happens automatically
# inside `skilldecks install`, no manual activation step required.
skilldecks install

# Free up a seat (e.g. retiring an old laptop):
skilldecks deactivate MKT-AGCY-MULT-CLNT
# Or release a different machine's seat (you'll need its fingerprint;
# the cap-exceeded message lists the active ones):
skilldecks deactivate MKT-AGCY-MULT-CLNT --fingerprint <fp>

# Inspect current usage:
skilldecks seats MKT-AGCY-MULT-CLNT

Cap-exceeded message

When the seat cap is full and you try to activate a new machine, skilldecks install exits with:

Seat limit reached for MKT-AGCY-MULT-CLNT: 5/5 in use.
Deactivate one of: <fp_a, fp_b, fp_c, fp_d, fp_e> via
`skilldecks deactivate MKT-AGCY-MULT-CLNT --fingerprint <fp>`, or
contact sales for a higher-seat license.

Cache, rotation, and growth

The customer-side cache is a single small JSON file per license. The server-side log is one <DATA_DIR>/activations/<license_id>.jsonl per license that grows by one line per (re)activation or deactivation. At v1 scale (a handful of machines per license over a multi-year window) growth is negligible. Operators who want hard caps on file size can run skilldecks_store.activation._compact_inplace on a daily cron — it rewrites each file with only the live records.

Vendored bundles

Third-party content is fetched into ~/.skilldecks/vendor/<bundle-name>/ at frozen SHAs (see skilldecks_installer/bundle_fetcher.py for current pins). We don't ship the 100MB+ payload in the wheel — it's pulled at skilldecks setup time. Bumping a pin in code requires customers to re-run skilldecks setup to pick up the change.

Local development

cd skilldecks-installer
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

Layout

skilldecks-installer/
├── pyproject.toml
├── skilldecks_installer/        # the Python package
│   ├── cli.py                # `skilldecks` CLI
│   ├── admin.py              # `skd-admin` operator CLI
│   ├── schema.py             # Pydantic models for license YAML
│   ├── license_loader.py     # reads + merges ~/.skilldecks/licenses/
│   ├── pip_runner.py         # subprocess wrapper for pip
│   ├── skill_copier.py       # copies SKILL.md from installed packages
│   ├── bundle_fetcher.py     # clones third-party bundles to ~/.skilldecks/vendor/
│   └── bundle_installer.py   # deploys bundles into ~/.claude/ (namespaced)
├── skill/
│   └── SKILL.md              # the Claude Code skill that delegates to `skilldecks`
├── scripts/
│   ├── install.sh            # the curl|sh one-liner
│   └── skus.yaml             # SKU registry (operator-side)
└── tests/
    ├── conftest.py
    ├── fixtures/licenses/    # sample license YAMLs for tests
    └── test_*.py

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skilldecks_installer-0.2.2.tar.gz (429.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skilldecks_installer-0.2.2-py3-none-any.whl (446.9 kB view details)

Uploaded Python 3

File details

Details for the file skilldecks_installer-0.2.2.tar.gz.

File metadata

  • Download URL: skilldecks_installer-0.2.2.tar.gz
  • Upload date:
  • Size: 429.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for skilldecks_installer-0.2.2.tar.gz
Algorithm Hash digest
SHA256 12c6bfda525f02ef4ece3f5629cdd87ba52292d0ea623157854985ada43518bd
MD5 b34887647cf13b1496f0f79400fe94ba
BLAKE2b-256 27841d40f01575b0a4f3638300d77b512bd93360d2dcb47517cb3e47b2cc2d25

See more details on using hashes here.

Provenance

The following attestation bundles were made for skilldecks_installer-0.2.2.tar.gz:

Publisher: publish-installer.yml on don-devo/skilldecks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file skilldecks_installer-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for skilldecks_installer-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8c1273a5a39bc6ab77d2918a6f86158dd16a514c60a32e8a02adda6723692d43
MD5 3da9ab3d3712cea385449254454500e4
BLAKE2b-256 f07fe21fd0f2e69ef03b7cbda353bf750a7f7534074172310e66abf8bc71d8ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for skilldecks_installer-0.2.2-py3-none-any.whl:

Publisher: publish-installer.yml on don-devo/skilldecks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page