Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
# datekeys-go
Reference implementation in Go of the **DateKeys Protocol Specification
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
v0.9** ([`spec/`](spec/DateKeys_Protocol_Specification_v0.9.md)).
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
[Versión en español ](README.es.md ).
DateKeys encrypts data so that it can only be opened after a chosen instant.
The time condition comes from the drand **Quicknet** randomness beacon: data is
sealed with timelock encryption to a future round, and the round's BLS
signature, published by drand when the round arrives, is the key. Everything
that can be verified locally is verified locally; relays, caches and APIs are
untrusted transports.
> **Status: v0.x, pre-standard.** The specification is a draft and the API may
> change before v1.0.0. The code has not had an external cryptographic review
> yet (spec §75). Do not rely on it for high-value secrets.
## What it implements
| Object | Spec | Package |
|---|---|---|
| DateKey: date → round, canonical `dk1_…` string | §14– §19 | [`datekey` ](datekey ) |
| Provider Profile, pinned Quicknet profile, `profile_hash` | §10– §13 | [`profile` ](profile ) |
| Release sources, local BLS verification, drand relays | §45– §52 | [`provider` ](provider ), [`provider/drand` ](provider/drand ) |
| DateKeyCap `.dkc` : `time_only` and `time_and_key` | §20– §39, §61– §63 | [`capsule` ](capsule ) |
| DateKeys Access Key `.dkk` | §40– §44 | [`accesskey` ](accesskey ) |
| Extensions | §54 | [`extension` ](extension ) |
| Deterministic CBOR | §58 | [`codec` ](codec ) |
| Normative errors | §69 | [`errors.go` ](errors.go ) |
| CLI | — | [`cmd/datekeys` ](cmd/datekeys ) |
No cryptography is implemented here. Encryption is [age ](https://age-encryption.org )
(`filippo.io/age`); the timelock is [tlock ](https://github.com/drand/tlock )
(its exported core only); BLS verification is drand's. This module adds
framing, CBOR, bindings, verification rules and the flow, and it enforces the
stanza rules of the protocol inside the age identities, so that a file is never
accepted just because age could unwrap a key.
Not implemented on purpose: the Release API server and queue, storage and
delivery, concrete extensions, and the TypeScript client (plan §2).
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
## Versions
Three version numbers, each with its own meaning:
| Version | Where | Changes when |
|---|---|---|
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Format | Inside the objects: the capsule format, the `VERSION` of the DKC1 prelude, 2 when written and 1 or 2 when read, which is also the schema version of CONTROL_CBOR; and 1 for the framing of DKK1 and the schema of the other objects | The format changes. A reader rejects a version it does not know (spec §22, §70) |
| Specification | `datekeys.SpecVersion` , today `0.9` , and the tag `spec-v0.9` | The normative text changes. Spec §76 records each change with its case |
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Module | The tags of this Go module, `vX.Y.Z` , and `datekeys.Version()` | The API or the behaviour changes. Semantic versioning, with no stability promise before v1.0.0 |
`datekeys version` prints the module version, the specification and the Go toolchain. A binary built in a checkout shows the pseudo-version of its commit, for example `v0.0.0-20260928105528-9ac9cd952f04` .
Each release states what it covers, here and in the [CHANGELOG ](CHANGELOG.md ). The code on `main` , not yet released, covers:
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- specification 0.9: it writes capsule format 2 and reads formats 1 and 2;
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- the pinned Quicknet profile, and any profile on the three drand schemes that tlock supports;
- encryption, inspection and opening, and the CLI;
- every shared vector and fixture of [`testdata/` ](testdata ).
The first tag, v0.1.0, comes once `go get` works from a clean machine.
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
## A capsule, in one picture
```text
.dkc = PRELUDE (16 B) || PUBLIC_HEADER (CBOR) || SEALED_CONTROL (age) || PAYLOAD_AGE (age, to EOF)
time_only: SEALED_CONTROL = age(tlock round R → CONTROL_CBOR)
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
time_and_key: SEALED_CONTROL = age(tlock round R → age(16 X25519 stanzas → CONTROL_CBOR))
CONTROL_CBOR = { header_binding = SHA-256(PRELUDE || PUBLIC_HEADER), I_PAYLOAD, extensions, L, padding rule }
PAYLOAD_AGE = age(X25519 R_PAYLOAD → your data || zeros up to P = rule(L)), streamed
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
```
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
This is format 2, the one `encrypt` writes (spec §22). Its 16 stanzas hold
from 1 to 16 credentials and a dummy in each slot left, in a random order, and
its payload is padded to P, so that until the unlock date nobody learns the
number of credentials or the exact length L (spec §29.1, §39, §55.2). Format
1, that of v0.8.2, has one stanza per credential and no padding; readers
still open it.
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
## CLI
```bash
go install g.activething.com/go/DateKeys/cmd/datekeys@latest
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
```
The module is served by the project's own Gitea, whose certificate Go does not
trust by default. Set `GOPRIVATE=g.activething.com` so that the Go proxy and
checksum database are bypassed for it, and either install the server's
certificate or, on a trusted network, set `GOINSECURE=g.activething.com` .
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
```bash
datekeys datekey resolve -at 2030-01-01T00:00:00Z
datekeys encrypt -at 2030-01-01T00:00:00Z -in letter.txt -out letter.dkc
datekeys encrypt -at 2030-01-01T00:00:00Z -policy time_and_key -dkk letter.dkk -in letter.txt -out letter.dkc
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
datekeys encrypt -at 2030-01-01T00:00:00Z -padding bloque256 -in letter.txt -out letter.dkc
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
datekeys inspect -in letter.dkc
datekeys decrypt -in letter.dkc -out letter.txt -dkk letter.dkk
datekeys profile hash
Version constants: the specification and the module
- datekeys.SpecVersion ("0.8.2") names the specification the module
implements. A test ties it to the spec file, its title and
spec/README.md, and TestCatalogueMatchesSpec and the vector files use
it (testkit.SpecVersion now aliases it), so the vectors regenerate
unchanged.
- datekeys.Version() is the version of the module as the go command
recorded it. That is a tag, or for a binary built in a checkout the
pseudo-version of its commit (for example
v0.0.0-20260928105528-9ac9cd952f04), or (devel) when it is unknown, as
in tests or under a replace directive to a directory. It works as the
main module and as a dependency, whatever the module path, which it
reads from the root package.
- `datekeys version` (also -version and --version) prints both and the
Go toolchain.
- README.md and README.es.md explain the three versions (format,
specification, module) and what the code on main covers.
traceability §70 and CHANGELOG follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
datekeys version
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
```
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`encrypt` never touches the network. It pads the content with the rule
reforzado, or bloque256 if asked (spec §29.1). `inspect` runs only the
pre-unlock checks (spec §63 steps 1– 8): it never requests a release and never
uses a secret.
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
`decrypt` fetches the release from public drand relays, verifies it locally
and publishes the plaintext only after age authenticated all of it. Outputs are
never overwritten.
## Library
```go
reg, err := profile.Default() // pinned Quicknet profile, checked against its profile_hash
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
// Encrypt: no network, the round is resolved locally. Format 2 seals the
// content length before the content: src must deliver exactly Length bytes.
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
res, err := capsule.Encrypt(dst, src, capsule.EncryptOptions{
Profile: profile.Quicknet(),
UnlockAt: time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC),
Policy: capsule.TimeAndKey,
NewPortableKey: true, // res.PortableKey is the .dkk; encode it with accesskey.Encode
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Length: size,
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
Now: time.Now,
})
// Inspect: steps 1– 8, no network, no secrets.
in, err := capsule.Inspect(f, capsule.InspectOptions{Registry: reg})
// Open: steps 1– 18; the release is verified locally.
opened, err := capsule.Open(ctx, tmp, f, capsule.OpenOptions{
Registry: reg,
Source: drand.New(),
AccessKey: key, // or Identities: []age.Identity{...}
Now: time.Now,
})
if errors.Is(err, datekeys.ErrReleaseUnavailable) { /* not yet */ }
```
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`Open` streams the content and never the padding; on error, discard what was
written and never present it as valid (spec §56). `opened.Format` is the
format of the capsule: format 1 hides neither the number of credentials nor
the exact length of the content, so show it (spec §70).
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
Every protocol failure wraps one of the 17 normative errors of spec §69, so
`errors.Is` and `datekeys.Code(err)` identify it.
## Security properties and limits
- **Time confidentiality** holds under drand's threshold assumption. The
Quicknet timelock is **not post-quantum** : ciphertexts kept for years are
exposed to harvest-now, decrypt-later (spec §7.7, §53).
- **No trust in servers**: the profile is pinned in the binary, the round is
computed locally, releases are BLS-verified locally, and a valid signature of
another round is rejected (spec §13, §17, §51).
Spec v0.8.2: second-round corrections from the formal review
The second round of the formal review confirmed the nine corrections of
c57ed48 and asked for these, recorded in §76 as corrections 4 to 6 and
an editorial note:
- §72: an encoder MUST NOT write a registered extension in an object or
array it is not registered for; §54: a reader MUST NOT interpret the
data of a noncritical one it ignores for that reason. capsule.Encrypt
and accesskey.Encode take no Registry, so the application applies the
rule; their documentation and extension.Placement say so.
- §17 and §51 give the step-10 codes only for a directly supplied
release, as step 10 does; a network source discards a failing one at
step 9.
- Step 9 reports ERR_RELEASE_UNAVAILABLE and no other code, whatever the
failure of the source. provider/drand.Client keeps each relay's failure
as text only (errors.Join made a relay's ERR_ROUND_MISMATCH match with
errors.Is), and capsule.Open keeps only the text of a source error that
carries another code (a caller's source failing with
ERR_RELEASE_INVALID gave that code at step 9). A context that ended
stays detectable: Fetch now has a single failure path, so the canceled
and deadline cases are deterministic.
- TestExtensionPlacement covers the noncritical array of a .dkk: with
the object-blind extension.CheckNoncritical at step 9.a it fails.
- Editorial: §28.1 "analizan solo la cabecera age", one arrow at step 9,
two §76 introductions; §73 lines for release sources and placement.
- testdata/README.md says the corpus registers its extensions in both
arrays of every object; traceability, CHANGELOG and both READMEs
(integrity holds against whoever lacks the file keys, §27, §55.1)
follow. Spec dated 28 September 2026; new SHA-256 in spec/README.md.
No fixture or vector changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- **Integrity**: framing, header, control and payload are authenticated
against whoever does not know the file keys: a third party's change makes
opening fail (fixtures and the mutation corpus). Once the round is
published anyone can compute the time file key, and anyone can seal a new
control for a public header; who can write each part, and from which step
it is bound, is the trust model of spec §27 and §55.1.
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- **Metadata privacy** (format 2): until the date, the exact length of the
content and the number of credentials stay hidden; the date, the access
policy, `capsule_id` , the header extensions and the padded size P do not
(spec §55.2).
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
- **No authorship**: `time_only` gives internal coherence, not proof of who
created a capsule, before or after it matures; `time_and_key` adds an access
barrier, not a signature (spec §36.1).
- **Recovery years later** needs the historical release: from a drand relay
that still serves it or from any cache, re-verified locally (spec §50).
See [SECURITY.md ](SECURITY.md ).
## Conformance and tests
```bash
go test ./... # unit, golden vectors, fixtures, mutation corpus
go test -race -cover ./...
go test -fuzz=FuzzInspect ./capsule # one fuzz target at a time
go test -tags interop ./capsule # official age and tle CLIs open our files
go test -tags integration ./capsule ./provider/drand # live Quicknet
```
- `testdata/vectors` : profile hash, date→round and `dk1_` vectors (spec §65, §66),
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
CBOR profile and schema vectors, padding vectors (spec §29.1), the exported
mutation corpus and a differential corpus of the pre-unlock checks; formats in
[`testdata/README.md` ](testdata/README.md ).
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
- `testdata/fixtures` : official `.dkc` /`.dkk` fixtures over published rounds,
with the BLS signature embedded and every intermediate value (spec §67, §68);
Implement capsule format 2 of spec v0.9
The reference moves to the DateKeys Protocol Specification v0.9, approved
by its author on 29 September 2026. Encrypt writes capsule format 2 only;
Open and Inspect read formats 1 and 2, and a format 1 capsule keeps the
verdict v0.8.2 gave it.
Format 2 (spec §22, §29.1, §31, §39):
- VERSION in the PRELUDE is the capsule format, capsule.Format; any other
value is ERR_UNSUPPORTED_VERSION at step 2.
- CONTROL_CBOR has the schema version of its format. Version 2 adds key 6,
payload_length (8 bytes, big-endian, at most L_MAX = 2^53 - 2^46), and
key 7, padding (1 bloque256, 2 reforzado); it is 103 bytes without
extensions, whatever L.
- The payload is the content padded with zeros to P = rule(L). Step 17
checks the length and the zeros, and Open writes only the first L bytes.
- INNER_ACCESS_AGE holds exactly 16 X25519 stanzas: 1 to 16 credentials,
and a dummy in each slot left, in a uniformly random order.
Writer rules (spec §62.1): EncryptOptions.Length is required and the
source must deliver exactly that many bytes; recipients that are not
canonical or of low order are rejected (agewrap.CheckX25519Recipient);
self-checks of the header, the control, INNER_ACCESS_AGE and PAYLOAD_AGE.
The CLI measures its input, takes -padding and reports the format.
Test data: seven format 2 fixtures, padding vectors checked against
math/big, format 2 CBOR vectors, and the mutation corpus in both formats
with the 22 cases of the third list of spec §64, built without randomness
by sealing the fixtures again with their known keys and nonces. The
format 1 fixtures are kept byte for byte and never regenerated; the
differential corpus keeps its 1825 cases and adds a block per format 2
fixture. The spec copy loses its "to be implemented" markers, and the
READMEs, CHANGELOG, traceability and testdata/README.md follow v0.9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
they decrypt offline. Seven are of format 2; the five of format 1, from
v0.8.2, are kept for compatibility. Each `.dkc` has its frozen
`datekeys inspect -json` output.
- `internal/testkit.Mutations` : the mutations of spec §64, the 33 of its first
two lists in each format and the 22 of its third list, and 37 more, each
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
with its exact error and step, and a check that pre-unlock failures never
cause a release request; exported to `testdata/vectors/mutations.json` .
Initial implementation of the DateKeys Protocol v0.8.1
Reference implementation in Go, built from the implementation plan
(milestones M0 to M5): datekey, profile, provider, codec, agewrap,
extension, capsule, accesskey, the datekeys CLI, official vectors and
fixtures, the mutation corpus, fuzz targets, interop and live tests,
CI workflows, traceability and policy documents.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2 weeks ago
- [`docs/traceability.md` ](docs/traceability.md ): spec section → code → test.
- [`spec/datekeys.cddl` ](spec/datekeys.cddl ): CBOR schemas.
## License
Code: Apache-2.0 ([LICENSE](LICENSE)). Specification: CC-BY-4.0
([spec/README.md](spec/README.md)). `codec/bech32` is copied from age under its
own license. "DateKeys" is a reserved name: see [TRADEMARKS.md ](TRADEMARKS.md ).