capsium 0.2.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +17 -1
- data/CHANGELOG.md +322 -0
- data/README.adoc +643 -7
- data/capsium.gemspec +3 -0
- data/lib/capsium/cli/formatting.rb +27 -0
- data/lib/capsium/cli/package.rb +106 -7
- data/lib/capsium/cli/reactor.rb +120 -6
- data/lib/capsium/cli.rb +30 -0
- data/lib/capsium/log_buffer.rb +47 -0
- data/lib/capsium/package/authentication.rb +38 -0
- data/lib/capsium/package/authentication_config.rb +78 -0
- data/lib/capsium/package/bundle.rb +136 -0
- data/lib/capsium/package/cipher.rb +253 -0
- data/lib/capsium/package/composition.rb +93 -0
- data/lib/capsium/package/dataset.rb +20 -0
- data/lib/capsium/package/dependency_resolver.rb +91 -0
- data/lib/capsium/package/encryption_config.rb +42 -0
- data/lib/capsium/package/manifest.rb +1 -1
- data/lib/capsium/package/merged_view.rb +170 -0
- data/lib/capsium/package/metadata.rb +1 -1
- data/lib/capsium/package/metadata_config.rb +9 -1
- data/lib/capsium/package/open_pgp.rb +65 -0
- data/lib/capsium/package/open_pgp_cipher.rb +66 -0
- data/lib/capsium/package/open_pgp_signer.rb +176 -0
- data/lib/capsium/package/preparation.rb +81 -0
- data/lib/capsium/package/routes_config.rb +60 -6
- data/lib/capsium/package/security.rb +21 -4
- data/lib/capsium/package/security_config.rb +6 -2
- data/lib/capsium/package/signer.rb +216 -0
- data/lib/capsium/package/storage_config.rb +29 -1
- data/lib/capsium/package/store.rb +136 -0
- data/lib/capsium/package/testing/config_test.rb +70 -0
- data/lib/capsium/package/testing/context.rb +11 -0
- data/lib/capsium/package/testing/data_validation_test.rb +63 -0
- data/lib/capsium/package/testing/file_test.rb +45 -0
- data/lib/capsium/package/testing/report.rb +33 -0
- data/lib/capsium/package/testing/route_test.rb +64 -0
- data/lib/capsium/package/testing/test_case.rb +60 -0
- data/lib/capsium/package/testing/test_suite.rb +86 -0
- data/lib/capsium/package/testing.rb +20 -0
- data/lib/capsium/package/validator.rb +39 -3
- data/lib/capsium/package/verification.rb +34 -0
- data/lib/capsium/package/version.rb +204 -0
- data/lib/capsium/package.rb +78 -75
- data/lib/capsium/packager.rb +97 -22
- data/lib/capsium/reactor/authenticator.rb +180 -0
- data/lib/capsium/reactor/content_api.rb +110 -0
- data/lib/capsium/reactor/data_api.rb +154 -0
- data/lib/capsium/reactor/deploy.rb +71 -0
- data/lib/capsium/reactor/endpoints.rb +37 -0
- data/lib/capsium/reactor/graphql_api.rb +123 -0
- data/lib/capsium/reactor/graphql_schema.rb +138 -0
- data/lib/capsium/reactor/htpasswd.rb +154 -0
- data/lib/capsium/reactor/introspection.rb +151 -31
- data/lib/capsium/reactor/metrics.rb +30 -0
- data/lib/capsium/reactor/mount.rb +206 -0
- data/lib/capsium/reactor/oauth2.rb +106 -0
- data/lib/capsium/reactor/overlay.rb +248 -0
- data/lib/capsium/reactor/package_saver.rb +99 -0
- data/lib/capsium/reactor/responses.rb +41 -0
- data/lib/capsium/reactor/serving.rb +154 -0
- data/lib/capsium/reactor/session.rb +82 -0
- data/lib/capsium/reactor.rb +116 -67
- data/lib/capsium/registry/local.rb +120 -0
- data/lib/capsium/registry/remote.rb +105 -0
- data/lib/capsium/registry.rb +190 -0
- data/lib/capsium/thor_ext.rb +1 -1
- data/lib/capsium/version.rb +1 -1
- data/lib/capsium.rb +2 -0
- data/sig/capsium/log_buffer.rbs +30 -0
- data/sig/capsium/package/authentication.rbs +23 -0
- data/sig/capsium/package/authentication_config.rbs +71 -0
- data/sig/capsium/package/bundle.rbs +43 -0
- data/sig/capsium/package/cipher.rbs +62 -0
- data/sig/capsium/package/composition.rbs +21 -0
- data/sig/capsium/package/dataset.rbs +8 -0
- data/sig/capsium/package/dependency_resolver.rbs +53 -0
- data/sig/capsium/package/encryption_config.rbs +39 -0
- data/sig/capsium/package/merged_view.rbs +47 -0
- data/sig/capsium/package/metadata.rbs +1 -0
- data/sig/capsium/package/open_pgp.rbs +38 -0
- data/sig/capsium/package/open_pgp_cipher.rbs +15 -0
- data/sig/capsium/package/open_pgp_signer.rbs +46 -0
- data/sig/capsium/package/preparation.rbs +23 -0
- data/sig/capsium/package/routes_config.rbs +36 -3
- data/sig/capsium/package/security.rbs +9 -2
- data/sig/capsium/package/security_config.rbs +6 -3
- data/sig/capsium/package/signer.rbs +66 -0
- data/sig/capsium/package/storage_config.rbs +26 -2
- data/sig/capsium/package/store.rbs +43 -0
- data/sig/capsium/package/testing/config_test.rbs +21 -0
- data/sig/capsium/package/testing/context.rbs +14 -0
- data/sig/capsium/package/testing/data_validation_test.rbs +22 -0
- data/sig/capsium/package/testing/file_test.rbs +17 -0
- data/sig/capsium/package/testing/report.rbs +20 -0
- data/sig/capsium/package/testing/route_test.rbs +22 -0
- data/sig/capsium/package/testing/test_case.rbs +41 -0
- data/sig/capsium/package/testing/test_suite.rbs +20 -0
- data/sig/capsium/package/testing.rbs +7 -0
- data/sig/capsium/package/verification.rbs +26 -0
- data/sig/capsium/package/version.rbs +39 -0
- data/sig/capsium/package.rbs +29 -16
- data/sig/capsium/packager.rbs +9 -0
- data/sig/capsium/reactor/authenticator.rbs +40 -0
- data/sig/capsium/reactor/content_api.rbs +19 -0
- data/sig/capsium/reactor/data_api.rbs +23 -0
- data/sig/capsium/reactor/deploy.rbs +34 -0
- data/sig/capsium/reactor/endpoints.rbs +16 -0
- data/sig/capsium/reactor/graphql_api.rbs +27 -0
- data/sig/capsium/reactor/graphql_schema.rbs +20 -0
- data/sig/capsium/reactor/htpasswd.rbs +36 -0
- data/sig/capsium/reactor/introspection.rbs +27 -7
- data/sig/capsium/reactor/metrics.rbs +13 -0
- data/sig/capsium/reactor/mount.rbs +83 -0
- data/sig/capsium/reactor/oauth2.rbs +31 -0
- data/sig/capsium/reactor/overlay.rbs +72 -0
- data/sig/capsium/reactor/package_saver.rbs +12 -0
- data/sig/capsium/reactor/responses.rbs +23 -0
- data/sig/capsium/reactor/serving.rbs +46 -0
- data/sig/capsium/reactor/session.rbs +36 -0
- data/sig/capsium/reactor.rbs +22 -2
- data/sig/capsium/registry/local.rbs +16 -0
- data/sig/capsium/registry/remote.rbs +18 -0
- data/sig/capsium/registry.rbs +83 -0
- data/sig/graphql.rbs +12 -0
- metadata +135 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d5f0f655cb66b0e6b49edcb70ad68b8e6670e0ca2cb0d5a77aef84c609a3c6b
|
|
4
|
+
data.tar.gz: 3f4f17221db2c72db65c14faa2b5e3a31cf026e5c5b1e0ffc331af872b8b0111
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4e3b9777dc6f0c498784959ab66cb89228657b6f8e85a43818c804c311371d86f78d9ffd3cf7f61e1ad743db08608fafdbe35d49d3936073f8d8281d66cd9816
|
|
7
|
+
data.tar.gz: 7cfb11cce09e70b03f94057c68adea6389022f50aa5b9573311f0cf0a8d606013bf60115e4f11bfb45a07460fd3dd5f22e93b34ef5dcb84cb052bc25c93cc6bb
|
data/.rubocop.yml
CHANGED
|
@@ -26,8 +26,24 @@ Metrics/BlockLength:
|
|
|
26
26
|
- 'spec/**/*'
|
|
27
27
|
- 'capsium.gemspec'
|
|
28
28
|
|
|
29
|
+
# Spec helpers take the parameters the fixtures need.
|
|
30
|
+
Metrics/ModuleLength:
|
|
31
|
+
Exclude:
|
|
32
|
+
- 'spec/**/*'
|
|
33
|
+
|
|
34
|
+
# Spec helpers take the parameters the fixtures need; the reactor
|
|
35
|
+
# constructor takes one keyword per deploy-time concern (keyword args
|
|
36
|
+
# are self-documenting and not counted).
|
|
37
|
+
Metrics/ParameterLists:
|
|
38
|
+
Max: 7
|
|
39
|
+
CountKeywordArgs: false
|
|
40
|
+
Exclude:
|
|
41
|
+
- 'spec/**/*'
|
|
42
|
+
|
|
29
43
|
Metrics/ClassLength:
|
|
30
|
-
|
|
44
|
+
# The reactor (request dispatch + lifecycle) and the introspection
|
|
45
|
+
# endpoint set grew with the phase-3 follow-ons.
|
|
46
|
+
Max: 150
|
|
31
47
|
|
|
32
48
|
Metrics/MethodLength:
|
|
33
49
|
Max: 25
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,327 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- OpenPGP signatures and encryption through librnp (new `rnp` gem
|
|
8
|
+
dependency, loaded lazily — a typed
|
|
9
|
+
`Capsium::Package::OpenPgp::OpenPgpUnavailableError` with
|
|
10
|
+
installation guidance is raised when the gem or librnp is missing):
|
|
11
|
+
- `Capsium::Package::OpenPgpSigner` (parallel to the RSA-SHA256/X.509
|
|
12
|
+
`Signer`, same construction semantics and `Signer::SignatureError`
|
|
13
|
+
taxonomy): `capsium package sign PATH --openpgp --key SEC.asc`
|
|
14
|
+
writes an armored detached OpenPGP signature (SHA-256) over the
|
|
15
|
+
canonical section-6a payload to `signature.sig`, embeds the armored
|
|
16
|
+
public key as `signature.pub.asc` and records
|
|
17
|
+
`"digitalSignatures": {"certificateType": "OpenPGP", ...}` in
|
|
18
|
+
security.json. Verification auto-detects the scheme from
|
|
19
|
+
`certificateType` (`Signer.verify_package`,
|
|
20
|
+
`Package#verify_signature`, the load-time signature gate and
|
|
21
|
+
`capsium package verify-signature` all dispatch); `--openpgp
|
|
22
|
+
--cert PUB.asc` verifies with an explicit OpenPGP public key.
|
|
23
|
+
- `Capsium::Package::OpenPgpCipher < Cipher` (same encrypted .cap
|
|
24
|
+
layout and AES-256-GCM content encryption): `capsium package
|
|
25
|
+
encrypt PATH --openpgp --recipient PUB.asc -o OUT.cap` protects the
|
|
26
|
+
DEK as an armored OpenPGP message
|
|
27
|
+
(`signature.json`: `{"encryption": {"algorithm": "AES-256-GCM",
|
|
28
|
+
"keyManagement": "OpenPGP", "message", "iv", "authTag"}}`).
|
|
29
|
+
Decryption auto-detects the key management from the envelope
|
|
30
|
+
(`Cipher.for_encrypted`, `Cipher.key_management`), and
|
|
31
|
+
`Package.new(decryption_key:)` accepts OpenPGP secret keys
|
|
32
|
+
(armored or binary) transparently. `Cipher`'s envelope/DEK handling
|
|
33
|
+
was refactored into subclass seams; the RSA-OAEP-SHA256 path is
|
|
34
|
+
unchanged.
|
|
35
|
+
- `Capsium::Package::OpenPgp` is the single lazy loader of the rnp
|
|
36
|
+
binding and the key-file loader (armor/format auto-detection,
|
|
37
|
+
`OpenPgp::KeyError` for unreadable or unsuitable keys).
|
|
38
|
+
- The OpenPGP specs skip cleanly when librnp cannot load, so CI
|
|
39
|
+
without librnp stays green; see README "OpenPGP support (librnp)".
|
|
40
|
+
- Encapsulated packages (bundled dependencies): `capsium package pack
|
|
41
|
+
--bundle-deps` (alias `--bundle`; `Packager#pack` option
|
|
42
|
+
`bundle_deps: true`) resolves every declared `metadata.dependencies`
|
|
43
|
+
entry through the store -> registry chain and embeds the resolved
|
|
44
|
+
`.cap` files inside the parent under `packages/`, producing a fully
|
|
45
|
+
self-contained package that activates with no store or registry. The
|
|
46
|
+
`packages/index.json` manifest maps each dependency GUID to its
|
|
47
|
+
embedded file, resolved version and SHA-256
|
|
48
|
+
(`Capsium::Package::Bundle`). Loading resolves bundled dependencies
|
|
49
|
+
FIRST — including from inside a parent `.cap` — before the store ->
|
|
50
|
+
registry chain, and passes the bundle down so re-declared transitive
|
|
51
|
+
dependencies resolve from it (one-level bundling policy: the parent's
|
|
52
|
+
`metadata.dependencies` must list the transitive closure). The
|
|
53
|
+
manifest SHA-256 is re-verified at resolution
|
|
54
|
+
(`Security::IntegrityError` on mismatch) and each bundled package's
|
|
55
|
+
own `security.json`/signature is verified at activation. `pack`
|
|
56
|
+
gained `--store`/`--registry` options; `capsium package validate`
|
|
57
|
+
resolves bundled dependency layers, so fallthrough routes of
|
|
58
|
+
encapsulated packages validate.
|
|
59
|
+
|
|
60
|
+
## 0.5.0
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- Multiple mounted packages per reactor: `capsium reactor serve`
|
|
65
|
+
accepts multiple sources — positional arguments, repeatable
|
|
66
|
+
`--mount PATH=SOURCE` options and/or a JSON mount config
|
|
67
|
+
(`--config FILE`: `{"mounts": [{"path", "source", "store"?}]}`).
|
|
68
|
+
Default mount points: the first source at `/`, each additional one at
|
|
69
|
+
`/<metadata.name>/`; duplicate prefixes raise
|
|
70
|
+
`Capsium::Reactor::MountConflictError`. Requests dispatch by
|
|
71
|
+
longest-prefix mount matching (`Capsium::Reactor::Mount`); the
|
|
72
|
+
`/api/v1/introspect/*` endpoints aggregate ALL mounted packages,
|
|
73
|
+
`/package/<name>/{status,metadata,logs}` resolve by package name
|
|
74
|
+
(404 for unknown names), metrics/logs stay reactor-global, and
|
|
75
|
+
`Capsium::Reactor#cleanup` cleans up every mounted package.
|
|
76
|
+
Single-source invocation is unchanged.
|
|
77
|
+
- Writable packages (ARCHITECTURE.md section 5a): a mounted package
|
|
78
|
+
whose metadata does not declare `"readOnly": true` gets an
|
|
79
|
+
append-only overlay layer in the reactor workdir (`--workdir DIR`,
|
|
80
|
+
default a temporary directory removed on cleanup), always the
|
|
81
|
+
topmost layer of the merged view (`MergedView` gained
|
|
82
|
+
`extra_layers`). The immutable base never changes on disk and every
|
|
83
|
+
write is visible on the next request (hot-swap); overlay state
|
|
84
|
+
(content files, `.capsium-tombstones`, per-dataset JSON operation
|
|
85
|
+
logs) persists in the workdir.
|
|
86
|
+
- REST CRUD over datasets (`Capsium::Reactor::DataApi`):
|
|
87
|
+
`POST /api/v1/data/<dataset>` appends an item (201 + Location +
|
|
88
|
+
stored item; id convention is the `id` field else the 1-based index
|
|
89
|
+
as a string), `GET /<id>` reads one item, `PUT /<id>` replaces and
|
|
90
|
+
`DELETE /<id>` deletes. Statuses: 400 for malformed JSON, 403 with a
|
|
91
|
+
clear body on read-only packages, 404 for unknown datasets/items,
|
|
92
|
+
405 for wrong verbs, 409 for duplicate/mismatched ids, 422 with the
|
|
93
|
+
schema errors when the candidate document violates the dataset's
|
|
94
|
+
JSON schema, 501 for SQLite datasets. Undeclared datasets are not
|
|
95
|
+
served and route-level `accessControl` still applies.
|
|
96
|
+
- Content writes (`Capsium::Reactor::ContentApi`): `PUT <route>`
|
|
97
|
+
creates/overwrites a content file (creating its route on demand;
|
|
98
|
+
text bodies only for v1) and `DELETE <route>` records a tombstone so
|
|
99
|
+
the path 404s even when a lower layer holds the file (404 when
|
|
100
|
+
nothing to delete).
|
|
101
|
+
- GraphQL (new `graphql` dependency): every mounted package with
|
|
102
|
+
datasets answers `<mount>/graphql` (POST, or GET with `?query=`)
|
|
103
|
+
with a schema auto-derived from the package's storage
|
|
104
|
+
(`Capsium::Reactor::GraphqlSchema`): a query field `<dataset>`
|
|
105
|
+
(list) with an optional `id:` argument (single item) plus
|
|
106
|
+
`create<Dataset>`/`update<Dataset>`/`delete<Dataset>` mutations
|
|
107
|
+
matching the REST semantics including schema validation. Item types
|
|
108
|
+
are inferred from the dataset's JSON schema when present, else map
|
|
109
|
+
to a permissive JSON scalar; SQLite datasets are skipped. Not-found
|
|
110
|
+
items, schema violations, duplicates and read-only mutations land in
|
|
111
|
+
the `errors` array (no 500s on user error).
|
|
112
|
+
- Save composite: `POST /package/<name>/save`
|
|
113
|
+
(`Capsium::Reactor::PackageSaver`) folds the base package plus its
|
|
114
|
+
overlay into a NEW versioned `.cap` (`<name>-<version+patch>.cap`)
|
|
115
|
+
in the workdir — overlay content files replace, tombstones delete,
|
|
116
|
+
dataset mutation logs replay into the dataset files, and manifest,
|
|
117
|
+
routes and security.json are regenerated — returning the `.cap`
|
|
118
|
+
path and its SHA-256. The saved package passes
|
|
119
|
+
`capsium package validate`. Read-only packages get 403, unknown
|
|
120
|
+
names 404, non-POST 405.
|
|
121
|
+
|
|
122
|
+
### Changed
|
|
123
|
+
|
|
124
|
+
- `Capsium::Reactor::Introspection.new` now takes the list of served
|
|
125
|
+
packages (aggregation); `/introspect/status` `packagesLoaded`
|
|
126
|
+
reflects the mount count.
|
|
127
|
+
- The reactor dispatch, endpoint and response handling are split into
|
|
128
|
+
`Capsium::Reactor::Serving` (mount request serving),
|
|
129
|
+
`Capsium::Reactor::Endpoints` (reactor-level endpoints) and
|
|
130
|
+
`Capsium::Reactor::Responses` (HTTP response writers).
|
|
131
|
+
- WEBrick's `ProcHandler` (GET/POST/PUT only) is extended with
|
|
132
|
+
`do_DELETE`/`do_PATCH` aliases so the writable-package REST verbs
|
|
133
|
+
reach the mounted procs.
|
|
134
|
+
- `Metrics/ParameterLists` rubocop no longer counts keyword args (the
|
|
135
|
+
reactor constructor takes one keyword per deploy-time concern).
|
|
136
|
+
|
|
137
|
+
## 0.4.0
|
|
138
|
+
|
|
139
|
+
### Added
|
|
140
|
+
|
|
141
|
+
- Static registries (ARCHITECTURE.md section 4a follow-on): a registry
|
|
142
|
+
is a directory or a static https base URL holding an `index.json`
|
|
143
|
+
(`packages` -> GUID -> `name` + `versions` -> version -> `file`,
|
|
144
|
+
`sha256`, `size`) with `.cap` files stored relative to the registry
|
|
145
|
+
root, so any static host (GitHub Pages, S3, nginx) can serve one.
|
|
146
|
+
`Capsium::Registry.fetch(ref)` returns `Registry::Local` (read-write
|
|
147
|
+
directory) or `Registry::Remote` (read-only https base URL over
|
|
148
|
+
net/http with redirect following and timeouts; plain http for
|
|
149
|
+
loopback hosts only). `Registry#resolve` picks the newest version
|
|
150
|
+
satisfying a semver constraint via the built-in
|
|
151
|
+
`Version`/`VersionRange` matcher.
|
|
152
|
+
- `Registry::Local#push`: validates the `.cap` with
|
|
153
|
+
`Capsium::Package::Validator` (`Registry::InvalidPackageError` on
|
|
154
|
+
failure), copies it into the registry directory and atomically
|
|
155
|
+
rewrites `index.json` (tmp + rename) with recomputed sha256 and size.
|
|
156
|
+
- `Registry#install`: fetches the resolved `.cap`, verifies it against
|
|
157
|
+
the sha256 declared in the index (`Registry::ChecksumMismatchError`
|
|
158
|
+
on mismatch) and installs it into the package store as
|
|
159
|
+
`<name>-<version>.cap`, atomically updating the store's own
|
|
160
|
+
`index.json` (`Package::Store#install`). All registry failures are
|
|
161
|
+
typed `Registry::RegistryError` subclasses
|
|
162
|
+
(`RegistryNotConfiguredError`, `InvalidRegistryError`,
|
|
163
|
+
`InvalidPackageError`, `PackageNotFoundError`,
|
|
164
|
+
`UnsatisfiableConstraintError`, `ChecksumMismatchError`,
|
|
165
|
+
`FetchError`).
|
|
166
|
+
- `capsium package push PACKAGE --registry DIR` and
|
|
167
|
+
`capsium install GUID [--constraint RANGE] [--registry DIR_OR_URL]
|
|
168
|
+
[--store DIR]`; the registry defaults to `CAPSIUM_REGISTRY`, the
|
|
169
|
+
store to `CAPSIUM_STORE` (typed errors when unconfigured).
|
|
170
|
+
- `capsium reactor serve capsium://GUID` (install-then-serve with
|
|
171
|
+
`--registry`/`--store`/`--constraint`), and dependency-resolution
|
|
172
|
+
fallback for composite packages: `Package::DependencyResolver` (and
|
|
173
|
+
`Capsium::Package.new`/`Capsium::Reactor.new` through their new
|
|
174
|
+
`registry:` keyword) installs a dependency from the configured
|
|
175
|
+
registry when the store has no package for its GUID — fallback chain
|
|
176
|
+
store -> registry -> typed error (`DependencyNotFoundError` /
|
|
177
|
+
`UnsatisfiableDependencyError`).
|
|
178
|
+
- Reactor-level and per-package introspection endpoints (ARCHITECTURE.md
|
|
179
|
+
section 7 follow-ons), extending `Capsium::Reactor::Introspection`:
|
|
180
|
+
`GET /introspect/status` (`{status, uptime, packagesLoaded}`),
|
|
181
|
+
`GET /introspect/config` (port, store dir, cacheControl, authEnabled,
|
|
182
|
+
registry — deploy.json values and registry URL credentials are never
|
|
183
|
+
exposed) and `GET /introspect/metrics`
|
|
184
|
+
(`{uptime, requestsTotal, requestsByStatus}`) backed by the new
|
|
185
|
+
thread-safe in-memory `Capsium::Reactor::Metrics` counter wired into
|
|
186
|
+
the serving path, plus `GET /package/<name>/status`,
|
|
187
|
+
`GET /package/<name>/metadata` and `GET /package/<name>/logs` (the
|
|
188
|
+
reactor serves one package; any other name is a 404). `logs` returns
|
|
189
|
+
the last N lines (`?lines=N`, default 100) from the new
|
|
190
|
+
`Capsium::LogBuffer`, a small thread-safe ring buffer recording key
|
|
191
|
+
serving events (reactor start, package reload, one line per served
|
|
192
|
+
request). All endpoints are GET-only JSON (405 otherwise) and gated
|
|
193
|
+
when `authentication.json` enables authentication.
|
|
194
|
+
|
|
195
|
+
### Changed
|
|
196
|
+
|
|
197
|
+
- `Metrics/ClassLength` rubocop budget raised to 150 for the reactor
|
|
198
|
+
and introspection classes grown by the phase-3 endpoints.
|
|
199
|
+
|
|
200
|
+
## 0.3.0
|
|
201
|
+
|
|
202
|
+
### Added
|
|
203
|
+
|
|
204
|
+
- Digital signatures (RSA-SHA256, X.509) per the packaging and security
|
|
205
|
+
standard clauses: `Capsium::Package::Signer` signs a package directory in
|
|
206
|
+
place (`signature.sig` plus the embedded public key PEM, recorded as
|
|
207
|
+
`security.digitalSignatures {publicKey, signatureFile}` in
|
|
208
|
+
`security.json`) and verifies declared signatures. The signed payload is
|
|
209
|
+
the concatenation, in sorted package-relative path order, of the raw
|
|
210
|
+
bytes of every checksum-covered file (everything except `security.json`
|
|
211
|
+
and `signature.sig`) — verifiable independently with
|
|
212
|
+
`openssl dgst -sha256 -verify`.
|
|
213
|
+
- Signed packages are verified automatically on load:
|
|
214
|
+
`Capsium::Package#verify_signature`/`#verify_signature!`/`#signed?`;
|
|
215
|
+
a mismatch raises `Capsium::Package::Signer::SignatureMismatchError`,
|
|
216
|
+
so the reactor refuses to serve tampered signed packages.
|
|
217
|
+
- `capsium package sign PATH --key KEY.pem [--cert CERT.pem]` and
|
|
218
|
+
`capsium package verify-signature PATH [--cert CERT.pem]` (exit status 1
|
|
219
|
+
on unsigned packages and verification failures). Both accept a package
|
|
220
|
+
directory or a `.cap` file.
|
|
221
|
+
- Whole-package encryption (AES-256-GCM, RSA-OAEP-SHA256 wrapped DEK) per
|
|
222
|
+
the packaging standard's encryption clause: `Capsium::Package::Cipher`
|
|
223
|
+
encrypts a package directory or `.cap` into the standard encrypted
|
|
224
|
+
layout (`metadata.json` cleartext, `signature.json` encryption envelope,
|
|
225
|
+
`package.enc` AES-256-GCM of the inner `.cap` zip) and decrypts it back.
|
|
226
|
+
The OCB/OpenPGP alternatives mentioned by the standard are intentionally
|
|
227
|
+
not implemented.
|
|
228
|
+
- `capsium package encrypt PATH --public-key PUB.pem -o OUT.cap` and
|
|
229
|
+
`capsium package decrypt PATH --private-key PRIV.pem [-o OUT.cap]`.
|
|
230
|
+
- `Capsium::Package.new(path, decryption_key:)` transparently decrypts an
|
|
231
|
+
encrypted `.cap` (or uncompressed encrypted directory) on load; without
|
|
232
|
+
a key it raises `Capsium::Package::Cipher::KeyRequiredError` (the
|
|
233
|
+
reactor refuses to serve), and a wrong key or tampered ciphertext raises
|
|
234
|
+
`Capsium::Package::Cipher::DecryptionError` (GCM authentication).
|
|
235
|
+
- `Capsium::Packager#transform_cap`/`#with_unpacked_cap` helpers for
|
|
236
|
+
in-place `.cap` transformations.
|
|
237
|
+
- The Capsium package testing YAML DSL (05x-testing): packages declare
|
|
238
|
+
tests in `tests/*.yaml` files (top-level `tests` list), and
|
|
239
|
+
`capsium package test PATH` runs them with per-test `PASS`/`FAIL`
|
|
240
|
+
output, a summary line and proper exit codes. Supported test types:
|
|
241
|
+
`route` (expected status, optional `response_contains` and
|
|
242
|
+
`expected_content_type` against a reactor started for the run),
|
|
243
|
+
`file` (existence, optional `contains`), `data_validation` (dataset
|
|
244
|
+
rows validated against a JSON schema, JSON and YAML formats) and
|
|
245
|
+
`config` (configuration files parsed and the known package configs
|
|
246
|
+
validated against their canonical models). Implemented as an
|
|
247
|
+
open/closed registry of `Capsium::Package::Testing::TestCase`
|
|
248
|
+
subclasses run by `Capsium::Package::Testing::TestSuite`; invalid
|
|
249
|
+
test definitions are reported as failures instead of aborting the run.
|
|
250
|
+
- Layered storage (ARCHITECTURE.md section 5a): `storage.layers` stacks
|
|
251
|
+
overlay directories over `content/` (bottom -> top in declaration
|
|
252
|
+
order), each mirroring the `content/` tree.
|
|
253
|
+
`Capsium::Package::MergedView` — the merged-view home shared by the
|
|
254
|
+
package (validation) and the reactor (serving) — resolves content
|
|
255
|
+
top-first, honors `.capsium-tombstones` deletions (a tombstoned path
|
|
256
|
+
resolves 404 even when a lower layer holds the file, and a tombstone
|
|
257
|
+
also suppresses dependency content below it), and hides
|
|
258
|
+
`visibility: private` layers and non-exported resources in the
|
|
259
|
+
exported-only view dependents get. Packages without a `layers` config
|
|
260
|
+
behave exactly as before (single implicit `content/` layer).
|
|
261
|
+
- Composite packages (ARCHITECTURE.md section 4a):
|
|
262
|
+
`metadata.dependencies` (GUID -> semver range) resolve against a
|
|
263
|
+
package store (`CAPSIUM_STORE` or `--store`) of
|
|
264
|
+
`<name>-<version>.cap` files plus an optional `index.json` (GUID ->
|
|
265
|
+
file); the newest satisfying version wins via the built-in semver
|
|
266
|
+
matcher `Capsium::Package::Version`/`VersionRange` (`>=`, `^`, `~`,
|
|
267
|
+
exact, `*`, `x`-wildcards/partials, comma/space conjunctions — no new
|
|
268
|
+
runtime dependency). Resolution is recursive with circularity
|
|
269
|
+
detection and raises typed errors:
|
|
270
|
+
`Capsium::Package::DependencyNotFoundError`,
|
|
271
|
+
`UnsatisfiableDependencyError`, `CircularDependencyError`,
|
|
272
|
+
`DependencyVisibilityError`.
|
|
273
|
+
- Resolved dependencies become read-only layers below all of the
|
|
274
|
+
dependent's own layers, and only `exported` resources are visible;
|
|
275
|
+
routes may address dependency content explicitly as
|
|
276
|
+
`"<dependency-guid>/<path>"` and declare the route-inheritance
|
|
277
|
+
attributes of 05x-routing: `remap` (replaces the serving path),
|
|
278
|
+
`responseRewrite` (`body`, `headers`), `responseHeaders` (merged over
|
|
279
|
+
served headers) and `requestHeaders` (parsed and exposed for
|
|
280
|
+
forwarding reactors; a documented no-op for this static reactor).
|
|
281
|
+
Referencing a dependency's private or missing resource is a load-time
|
|
282
|
+
error. `capsium package info` prints the resolved dependency tree;
|
|
283
|
+
`capsium reactor serve` accepts `--store`.
|
|
284
|
+
- Authentication (05x-authentication, ARCHITECTURE.md section 4b):
|
|
285
|
+
`authentication.json` declares `basicAuth {enabled, passwdFile,
|
|
286
|
+
realm}` and `oauth2 {enabled, provider, clientId, authorizationUrl,
|
|
287
|
+
tokenUrl, userinfoUrl, redirectPath, scopes}`; the reactor enforces
|
|
288
|
+
it via `Capsium::Reactor::Authenticator`. Basic authentication
|
|
289
|
+
challenges (401 + `WWW-Authenticate`) and verifies against the
|
|
290
|
+
htpasswd file — bcrypt via the new `bcrypt` runtime dependency, plus
|
|
291
|
+
pure-Ruby md5-crypt (`$apr1$`/`$1$` as deployed by
|
|
292
|
+
htpasswd/OpenSSL/glibc), `{SHA}` and a `crypt(3)` fallback.
|
|
293
|
+
OAuth2 runs the authorization-code flow over net/http: `/auth/login`
|
|
294
|
+
redirects with HMAC-signed state, the callback exchanges the code at
|
|
295
|
+
`tokenUrl`, fetches the userinfo claims and establishes an
|
|
296
|
+
HMAC-SHA256 signed session cookie (`Capsium::Reactor::Session`).
|
|
297
|
+
Secrets never come from the package: `clientSecret`/`sessionSecret`
|
|
298
|
+
and role assignments live in `deploy.json` (`--deploy` or
|
|
299
|
+
`CAPSIUM_DEPLOY`); without a configured session secret the reactor
|
|
300
|
+
generates one and persists it (mode 0600) outside the package.
|
|
301
|
+
Dataset-route `accessControl {roles, authenticationRequired}` is
|
|
302
|
+
enforced after authentication: 401 unauthenticated, 403 unauthorized.
|
|
303
|
+
- The `content-validity` introspection entry now reports `signed` and
|
|
304
|
+
`encrypted` status, plus `signatureValid` when the package declares a
|
|
305
|
+
signature; `Capsium::Package#encrypted?` reports whether the package
|
|
306
|
+
was loaded from an encrypted source.
|
|
307
|
+
|
|
308
|
+
### Changed
|
|
309
|
+
|
|
310
|
+
- Integrity checksums now exclude `signature.sig` in addition to
|
|
311
|
+
`security.json` (the signature signs the checksum-covered payload, so it
|
|
312
|
+
cannot be part of it).
|
|
313
|
+
- `capsium package pack` drops signing artifacts (`signature.sig`,
|
|
314
|
+
embedded public key) when repacking: signing is a post-pack step.
|
|
315
|
+
- The reactor serves resources through `Package#merged_view` (the
|
|
316
|
+
overlay-aware resolution path); behavior is unchanged for packages
|
|
317
|
+
without layers.
|
|
318
|
+
- Loading a package with declared `metadata.dependencies` now resolves
|
|
319
|
+
them eagerly against the package store (`store:` or `CAPSIUM_STORE`);
|
|
320
|
+
a package whose dependencies cannot resolve (including normalized
|
|
321
|
+
pre-0.2 dependency declarations) fails to load with a typed error.
|
|
322
|
+
- `Metrics/ParameterLists` rubocop budget raised to 6 for the reactor
|
|
323
|
+
constructor's per-concern keywords.
|
|
324
|
+
|
|
3
325
|
## 0.2.0
|
|
4
326
|
|
|
5
327
|
### Changed
|