flechtctl 0.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ *.egg-info
2
+ .cache/
3
+ .coverage
4
+ .coverage.*
5
+ .hypothesis/
6
+ .idea/
7
+ .pytest_cache/
8
+ .venv
9
+ .vscode/
10
+ /.claude/settings.local.json
11
+ /CLAUDE.local.md
12
+ /graphify-out/
13
+ /site/
14
+ __pycache__/
15
+ build/
16
+ coverage.xml
17
+ dist/
18
+ htmlcov/
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.5
2
+ Name: flechtctl
3
+ Version: 0.16.0
4
+ Summary: Keyrings and flenc:/flsig: secret tokens for Flechtwerk, without the framework: a library and a command line tool.
5
+ Project-URL: Documentation, https://bsure-analytics.github.io/flechtwerk/guides/flechtctl/
6
+ Project-URL: Homepage, https://github.com/bsure-analytics/flechtwerk
7
+ Project-URL: Issues, https://github.com/bsure-analytics/flechtwerk/issues
8
+ Project-URL: Repository, https://github.com/bsure-analytics/flechtwerk
9
+ Author: Christian Schlichtherle, Viktor Penev
10
+ Maintainer: b.sure GmbH
11
+ License: MIT
12
+ Keywords: flechtwerk,jwe,jws,keyring,secrets
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Security :: Cryptography
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: joserfc~=1.7
23
+ Description-Content-Type: text/markdown
24
+
25
+ # flechtctl
26
+
27
+ Keyrings and `flenc:` / `flsig:` secret tokens for
28
+ [Flechtwerk](https://github.com/bsure-analytics/flechtwerk) — as a small library and a
29
+ command line tool, **without the framework**. `flechtctl` depends on
30
+ [joserfc](https://jose.authlib.org/) only; it never imports `flechtwerk`, so it installs and
31
+ runs wherever Python does (including interpreters the full stream-processing framework does
32
+ not support yet).
33
+
34
+ It does three things:
35
+
36
+ - **Keyrings.** Create and edit keyring files — an RFC 7517 JWK Set holding oct (symmetric),
37
+ X25519 (sealing) and Ed25519 (signing) keys, each optionally bound to scopes. Optionally
38
+ password-protected.
39
+ - **Value crypto.** Seal, open, sign and verify values: `flenc:jwe:…` compact JWE tokens and
40
+ `flsig:jws:…` Ed25519 compact JWS tokens.
41
+ - **Token tooling.** Inspect a token's header, re-seal a token under another key.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ uvx flechtctl --help # run it without installing
47
+ uv tool install flechtctl # or install the command
48
+ pip install flechtctl # or use it as a library
49
+ ```
50
+
51
+ Python 3.12 or newer.
52
+
53
+ ## Command line quick start
54
+
55
+ ```bash
56
+ flechtctl keyring new keyring.json --kid prod-2026-10 # a keyring with one fresh 32-byte key
57
+ TOKEN=$(flechtctl encrypt -k keyring.json --scope api_key --string 's3cret')
58
+ flechtctl inspect "$TOKEN" # the unauthenticated header: kid, scope, alg
59
+ flechtctl decrypt -k keyring.json --scope api_key --string "$TOKEN"
60
+ ```
61
+
62
+ `-k/--keyring` is required for every command that needs key material: the command reads no
63
+ environment variables. `flechtctl keyring --help` lists the keyring commands — `new`, `add`,
64
+ `remove`, `primary`, `signing`, `show`, `export`, `import`, `protect` and `unprotect`. A keyring
65
+ file works like a keystore: a set of named entries, handed on one public half at a time with
66
+ `export` and `import`.
67
+
68
+ ## Library quick start
69
+
70
+ The functions take the keyring explicitly — there is no global state:
71
+
72
+ ```python
73
+ from pathlib import Path
74
+
75
+ from flechtctl import Keyring, decrypt, encrypt, sign, verify
76
+
77
+ keyring = Keyring.from_json(Path("keyring.json").read_text())
78
+
79
+ token = encrypt(keyring, "s3cret", scope="api_key") # "flenc:jwe:…"
80
+ assert decrypt(keyring, token, scope="api_key") == "s3cret"
81
+
82
+ signed = sign(keyring, {"order": 17}, scope="orders") # "flsig:jws:…" — needs a signing key for the scope
83
+ assert verify(keyring, signed, scope="orders") == {"order": 17}
84
+ ```
85
+
86
+ `encrypt`, `decrypt`, `sign` and `verify` work on any JSON value; the `*_bytes` variants take
87
+ raw payload bytes. `reencrypt` moves a token to the scope's current sealing key, `read_header`
88
+ shows a token's unauthenticated header, and `generate_oct_key`, `generate_x25519_key` and
89
+ `generate_ed25519_key` mint key material.
90
+
91
+ ## Equivalence with Flechtwerk's codecs
92
+
93
+ `flechtctl.encrypt(keyring, value, scope=s)` writes exactly the token that Flechtwerk's
94
+ `ENCRYPTED(inner, scope=s)` codec writes for `value == inner.encode(x)`, and every token
95
+ Flechtwerk writes opens here. For a `BYTES` field, pass the base64 string. Flechtwerk itself
96
+ is built on this package: `pip install "flechtwerk[secrets]"` installs the matching `flechtctl`.
97
+
98
+ ## More
99
+
100
+ The guide [Keyrings and Secret Tokens with flechtctl](https://bsure-analytics.github.io/flechtwerk/guides/flechtctl/)
101
+ walks through a complete reader/writer setup, password-protected keyrings and the library API.
102
+ The format itself is specified on the
103
+ [Encrypted Secrets](https://bsure-analytics.github.io/flechtwerk/concepts/secrets/) page.
104
+
105
+ MIT licensed.
@@ -0,0 +1,81 @@
1
+ # flechtctl
2
+
3
+ Keyrings and `flenc:` / `flsig:` secret tokens for
4
+ [Flechtwerk](https://github.com/bsure-analytics/flechtwerk) — as a small library and a
5
+ command line tool, **without the framework**. `flechtctl` depends on
6
+ [joserfc](https://jose.authlib.org/) only; it never imports `flechtwerk`, so it installs and
7
+ runs wherever Python does (including interpreters the full stream-processing framework does
8
+ not support yet).
9
+
10
+ It does three things:
11
+
12
+ - **Keyrings.** Create and edit keyring files — an RFC 7517 JWK Set holding oct (symmetric),
13
+ X25519 (sealing) and Ed25519 (signing) keys, each optionally bound to scopes. Optionally
14
+ password-protected.
15
+ - **Value crypto.** Seal, open, sign and verify values: `flenc:jwe:…` compact JWE tokens and
16
+ `flsig:jws:…` Ed25519 compact JWS tokens.
17
+ - **Token tooling.** Inspect a token's header, re-seal a token under another key.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ uvx flechtctl --help # run it without installing
23
+ uv tool install flechtctl # or install the command
24
+ pip install flechtctl # or use it as a library
25
+ ```
26
+
27
+ Python 3.12 or newer.
28
+
29
+ ## Command line quick start
30
+
31
+ ```bash
32
+ flechtctl keyring new keyring.json --kid prod-2026-10 # a keyring with one fresh 32-byte key
33
+ TOKEN=$(flechtctl encrypt -k keyring.json --scope api_key --string 's3cret')
34
+ flechtctl inspect "$TOKEN" # the unauthenticated header: kid, scope, alg
35
+ flechtctl decrypt -k keyring.json --scope api_key --string "$TOKEN"
36
+ ```
37
+
38
+ `-k/--keyring` is required for every command that needs key material: the command reads no
39
+ environment variables. `flechtctl keyring --help` lists the keyring commands — `new`, `add`,
40
+ `remove`, `primary`, `signing`, `show`, `export`, `import`, `protect` and `unprotect`. A keyring
41
+ file works like a keystore: a set of named entries, handed on one public half at a time with
42
+ `export` and `import`.
43
+
44
+ ## Library quick start
45
+
46
+ The functions take the keyring explicitly — there is no global state:
47
+
48
+ ```python
49
+ from pathlib import Path
50
+
51
+ from flechtctl import Keyring, decrypt, encrypt, sign, verify
52
+
53
+ keyring = Keyring.from_json(Path("keyring.json").read_text())
54
+
55
+ token = encrypt(keyring, "s3cret", scope="api_key") # "flenc:jwe:…"
56
+ assert decrypt(keyring, token, scope="api_key") == "s3cret"
57
+
58
+ signed = sign(keyring, {"order": 17}, scope="orders") # "flsig:jws:…" — needs a signing key for the scope
59
+ assert verify(keyring, signed, scope="orders") == {"order": 17}
60
+ ```
61
+
62
+ `encrypt`, `decrypt`, `sign` and `verify` work on any JSON value; the `*_bytes` variants take
63
+ raw payload bytes. `reencrypt` moves a token to the scope's current sealing key, `read_header`
64
+ shows a token's unauthenticated header, and `generate_oct_key`, `generate_x25519_key` and
65
+ `generate_ed25519_key` mint key material.
66
+
67
+ ## Equivalence with Flechtwerk's codecs
68
+
69
+ `flechtctl.encrypt(keyring, value, scope=s)` writes exactly the token that Flechtwerk's
70
+ `ENCRYPTED(inner, scope=s)` codec writes for `value == inner.encode(x)`, and every token
71
+ Flechtwerk writes opens here. For a `BYTES` field, pass the base64 string. Flechtwerk itself
72
+ is built on this package: `pip install "flechtwerk[secrets]"` installs the matching `flechtctl`.
73
+
74
+ ## More
75
+
76
+ The guide [Keyrings and Secret Tokens with flechtctl](https://bsure-analytics.github.io/flechtwerk/guides/flechtctl/)
77
+ walks through a complete reader/writer setup, password-protected keyrings and the library API.
78
+ The format itself is specified on the
79
+ [Encrypted Secrets](https://bsure-analytics.github.io/flechtwerk/concepts/secrets/) page.
80
+
81
+ MIT licensed.
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ build-backend = "hatchling.build"
3
+ requires = ["hatchling", "hatch-vcs"]
4
+
5
+ [project]
6
+ authors = [
7
+ { name = "Christian Schlichtherle" },
8
+ { name = "Viktor Penev" },
9
+ ]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Programming Language :: Python :: 3.13",
17
+ "Programming Language :: Python :: 3.14",
18
+ "Topic :: Security :: Cryptography",
19
+ ]
20
+ dependencies = [
21
+ "joserfc~=1.7",
22
+ ]
23
+ description = "Keyrings and flenc:/flsig: secret tokens for Flechtwerk, without the framework: a library and a command line tool."
24
+ dynamic = ["version"]
25
+ keywords = ["flechtwerk", "jwe", "jws", "keyring", "secrets"]
26
+ license = { text = "MIT" }
27
+ maintainers = [
28
+ { name = "b.sure GmbH" },
29
+ ]
30
+ name = "flechtctl"
31
+ readme = "README.md"
32
+ requires-python = ">=3.12"
33
+
34
+ [project.scripts]
35
+ flechtctl = "flechtctl.cli:main"
36
+
37
+ [project.urls]
38
+ Documentation = "https://bsure-analytics.github.io/flechtwerk/guides/flechtctl/"
39
+ Homepage = "https://github.com/bsure-analytics/flechtwerk"
40
+ Issues = "https://github.com/bsure-analytics/flechtwerk/issues"
41
+ Repository = "https://github.com/bsure-analytics/flechtwerk"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/flechtctl"]
45
+
46
+ [tool.hatch.version]
47
+ raw-options = { root = ".." }
48
+ source = "vcs"
@@ -0,0 +1,90 @@
1
+ """Keyrings and `flenc:`/`flsig:` secret tokens for Flechtwerk — without the framework.
2
+
3
+ This package owns the keyring model (`Keyring`, `X25519Key`, `Ed25519Key`) and
4
+ the token format (seal, open, sign, verify, the scope ratchet, the key-binding
5
+ checks), and depends on joserfc only — never on flechtwerk. The API takes the
6
+ keyring explicitly, with no process-global state:
7
+
8
+ from flechtctl import Keyring, decrypt, encrypt
9
+
10
+ keyring = Keyring.from_json(open("keyring.json").read())
11
+ token = encrypt(keyring, "s3cret", scope="api_key") # flenc:jwe:...
12
+ decrypt(keyring, token, scope="api_key") # "s3cret"
13
+
14
+ `encrypt(k, v, scope=s)` writes the same token flechtwerk's
15
+ `ENCRYPTED(inner, scope=s)` codec writes for `v == inner.encode(x)` — for a
16
+ `BYTES` field, pass the base64 string. The `flechtctl` command wraps the same
17
+ functions for the shell (`flechtctl --help`).
18
+
19
+ `import flechtctl` loads no joserfc: the keyring and error names are imported
20
+ eagerly, every `flechtctl.tokens` name lazily on first access (PEP 562).
21
+ """
22
+ import importlib
23
+
24
+ from .errors import (
25
+ PlaintextSecretError,
26
+ SecretDecryptError,
27
+ SecretError,
28
+ SecretFormatError,
29
+ SecretSignatureError,
30
+ UnsignedSecretError,
31
+ )
32
+ from .keyring import (
33
+ Ed25519Key,
34
+ KEY_BYTES,
35
+ Keyring,
36
+ KeyringError,
37
+ UnknownKeyError,
38
+ X25519Key,
39
+ is_encrypted_keyring,
40
+ keyring_from_jwk,
41
+ )
42
+
43
+ __all__ = [
44
+ "Ed25519Key",
45
+ "KEY_BYTES",
46
+ "Keyring",
47
+ "KeyringError",
48
+ "PREFIX",
49
+ "PlaintextSecretError",
50
+ "SCHEME",
51
+ "SIGNATURE_PREFIX",
52
+ "SIGNATURE_SCHEME",
53
+ "SecretDecryptError",
54
+ "SecretError",
55
+ "SecretFormatError",
56
+ "SecretSignatureError",
57
+ "UnknownKeyError",
58
+ "UnsignedSecretError",
59
+ "X25519Key",
60
+ "canonical_json",
61
+ "decrypt",
62
+ "decrypt_bytes",
63
+ "decrypt_keyring",
64
+ "encrypt",
65
+ "encrypt_bytes",
66
+ "encrypt_keyring",
67
+ "generate_ed25519_key",
68
+ "generate_oct_key",
69
+ "generate_x25519_key",
70
+ "is_encrypted",
71
+ "is_encrypted_keyring",
72
+ "is_signed",
73
+ "keyring_from_jwk",
74
+ "kid_of",
75
+ "read_header",
76
+ "reencrypt",
77
+ "sign",
78
+ "sign_bytes",
79
+ "signer_of",
80
+ "verify",
81
+ "verify_bytes",
82
+ ]
83
+
84
+ _TOKEN_NAMES = frozenset(name for name in __all__ if name not in globals()) # everything the eager imports did not bind
85
+
86
+
87
+ def __getattr__(name: str) -> object:
88
+ if name in _TOKEN_NAMES:
89
+ return getattr(importlib.import_module(".tokens", __name__), name)
90
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,7 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ __all__: list[str] = [] # purely internal: `python -m flechtctl` is an entry point, not an API
6
+
7
+ sys.exit(main())