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>
v0.9
dev 1 week ago
parent 9ac9cd952f
commit 5b342d32dc

@ -329,6 +329,10 @@ during the request); `datekeys.TestCode`.
### Added
- `datekeys.SpecVersion` (`0.8.2`), the specification the module implements,
and `datekeys.Version()`, the version of the module as the go command
recorded it: a tag, the pseudo-version of the commit of a checkout build,
or `(devel)`. `datekeys version` prints both and the Go toolchain.
- `capsule.OpenOptions.AccessKeyFile`, a `.dkk` still encoded, which `Open`
decodes at step 9.a and only for a `time_and_key` capsule (§63, §69.1).
- `ErrExtensionDataInvalid` (`ERR_EXTENSION_DATA_INVALID`, §69).

@ -40,6 +40,26 @@ No implementa, a propósito: el servidor y la cola de la Release API, el
almacenamiento y la entrega, extensiones concretas ni el cliente TypeScript
(plan §2).
## Versiones
Hay tres números de versión, cada uno con su significado:
| Versión | Dónde | Cambia cuando |
|---|---|---|
| Formato | Dentro de los objetos: la versión de framing de DKC1 y DKK1 y la versión de schema de la clave 1, hoy todas 1 | Cambia el formato. Un lector rechaza una versión que no conoce (spec §70) |
| Especificación | `datekeys.SpecVersion`, hoy `0.8.2`, y el tag `spec-v0.8.2` | Cambia el texto normativo. §76 del spec recoge cada cambio con su caso |
| Módulo | Los tags de este módulo Go, `vX.Y.Z`, y `datekeys.Version()` | Cambia la API o el comportamiento. Versionado semántico, sin promesa de estabilidad antes de v1.0.0 |
`datekeys version` imprime la versión del módulo, la del spec y la del toolchain de Go. Un binario compilado en un checkout muestra la pseudo-versión de su commit, por ejemplo `v0.0.0-20260928105528-9ac9cd952f04`.
Cada release dice qué cubre, aquí y en el [CHANGELOG](CHANGELOG.md). El código de `main`, aún sin publicar, cubre:
- la especificación 0.8.2, con versiones de formato 1;
- el perfil Quicknet pinneado, y cualquier perfil de los tres schemes de drand que soporta tlock;
- cifrado, inspección y apertura, y la CLI;
- todos los vectores y fixtures compartidos de [`testdata/`](testdata).
El primer tag, v0.1.0, llegará cuando `go get` funcione desde una máquina limpia.
## Una cápsula, en un dibujo
```text
@ -69,6 +89,7 @@ datekeys encrypt -at 2030-01-01T00:00:00Z -policy time_and_key -dkk carta.dkk -i
datekeys inspect -in carta.dkc
datekeys decrypt -in carta.dkc -out carta.txt -dkk carta.dkk
datekeys profile hash
datekeys version
```
`encrypt` nunca usa la red. `inspect` ejecuta solo las comprobaciones previas

@ -39,6 +39,26 @@ 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).
## Versions
Three version numbers, each with its own meaning:
| Version | Where | Changes when |
|---|---|---|
| Format | Inside the objects: the framing version of DKC1 and DKK1 and the schema version at key 1, all 1 today | The format changes. A reader rejects a version it does not know (spec §70) |
| Specification | `datekeys.SpecVersion`, today `0.8.2`, and the tag `spec-v0.8.2` | The normative text changes. Spec §76 records each change with its case |
| 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:
- specification 0.8.2, with format versions 1;
- 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.
## A capsule, in one picture
```text
@ -68,6 +88,7 @@ datekeys encrypt -at 2030-01-01T00:00:00Z -policy time_and_key -dkk letter.dkk -
datekeys inspect -in letter.dkc
datekeys decrypt -in letter.dkc -out letter.txt -dkk letter.dkk
datekeys profile hash
datekeys version
```
`encrypt` never touches the network. `inspect` runs only the pre-unlock checks

@ -6,6 +6,7 @@
// datekeys decrypt -in secret.dkc -out secret.txt [-dkk key.dkk] [-identity key.txt]
// datekeys datekey resolve -at 2030-01-01T00:00:00Z
// datekeys profile hash
// datekeys version
//
// Encryption never touches the network. Decryption fetches the release from
// public drand relays and verifies it locally. Outputs are written to a
@ -22,6 +23,7 @@ import (
"fmt"
"io"
"os"
"runtime"
"strings"
"time"
@ -42,6 +44,7 @@ const usage = `usage:
datekeys inspect -in FILE.dkc [-json]
datekeys datekey resolve -at TIME
datekeys profile hash [-in PROFILE.cbor]
datekeys version
TIME is RFC 3339 with a time zone, for example 2030-01-01T00:00:00Z.`
@ -94,6 +97,15 @@ func run(args []string, stdout, stderr io.Writer, now func() time.Time) error {
return errUsage
}
return profileHash(args[2:], stdout)
case "version", "-version", "--version":
if len(args) != 1 {
return errUsage
}
// The module version (a tag, or the pseudo-version of the commit a
// checkout was built from), the specification it implements and the
// toolchain.
fmt.Fprintf(stdout, "datekeys %s\nspecification %s\n%s %s/%s\n", datekeys.Version(), datekeys.SpecVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH)
return nil
case "-h", "-help", "--help", "help":
fmt.Fprintln(stdout, usage)
return nil

@ -11,6 +11,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
@ -281,13 +282,31 @@ func TestResolveAndProfile(t *testing.T) {
}
func TestUsage(t *testing.T) {
for _, args := range [][]string{nil, {"nope"}, {"datekey"}, {"profile", "x"}, {"encrypt", "-bogus"}, {"inspect", "extra"}} {
for _, args := range [][]string{nil, {"nope"}, {"datekey"}, {"profile", "x"}, {"encrypt", "-bogus"}, {"inspect", "extra"}, {"version", "extra"}} {
if _, _, err := cli(t, later, args...); err == nil {
t.Errorf("%v accepted", args)
}
}
}
// The version command names the module version, the specification and the
// toolchain; in a test the module version is unknown.
func TestVersion(t *testing.T) {
for _, arg := range []string{"version", "-version", "--version"} {
out, _, err := cli(t, later, arg)
if err != nil {
t.Fatal(err)
}
want := "datekeys " + datekeys.Version() + "\nspecification " + datekeys.SpecVersion + "\n" + runtime.Version() + " " + runtime.GOOS + "/" + runtime.GOARCH + "\n"
if out != want {
t.Errorf("%s: %q, want %q", arg, out, want)
}
}
if !strings.Contains(usage, "datekeys version") {
t.Error("the usage does not list the version command")
}
}
// Spec §53: long horizons get the harvest-now, decrypt-later warning.
func TestLongHorizonWarning(t *testing.T) {
dir := t.TempDir()

@ -12,7 +12,7 @@ import (
// The catalogue matches spec §69 exactly, in order.
func TestCatalogueMatchesSpec(t *testing.T) {
spec, err := os.ReadFile("spec/DateKeys_Protocol_Specification_v0.8.2.md")
spec, err := os.ReadFile("spec/DateKeys_Protocol_Specification_v" + datekeys.SpecVersion + ".md")
if err != nil {
t.Fatal(err)
}

@ -12,8 +12,9 @@ import (
"g.activething.com/go/DateKeys/profile"
)
// SpecVersion is the specification the vectors and fixtures implement.
const SpecVersion = "0.8.2"
// SpecVersion is the specification the vectors and fixtures implement: the
// one the module implements.
const SpecVersion = datekeys.SpecVersion
// RoundVector is one Quicknet resolution vector (spec §65).
type RoundVector struct {

@ -0,0 +1,50 @@
package datekeys
import (
"reflect"
"runtime/debug"
)
// SpecVersion is the version of the DateKeys Protocol Specification that this
// module implements: spec/DateKeys_Protocol_Specification_v0.8.2.md, tagged
// spec-v0.8.2 in its repository. It is neither the version of the module
// (see Version) nor the framing and schema versions inside the objects,
// which are 1 (spec §70).
const SpecVersion = "0.8.2"
// modulePath is the path of this module: the import path of its root
// package, whatever the module is called.
var modulePath = reflect.TypeFor[Error]().PkgPath()
// Version returns the version of this module in the running program, as the
// go command recorded it: a release tag such as v0.1.0; a pseudo-version,
// which names the commit, for a program built in a checkout of the module;
// or "(devel)" when it is unknown, as in tests or under a replace directive
// that points to a directory.
func Version() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "(devel)"
}
return versionIn(info)
}
func versionIn(info *debug.BuildInfo) string {
m := &info.Main
if m.Path != modulePath {
m = nil
for _, d := range info.Deps {
if d.Path == modulePath {
m = d
break
}
}
}
if m != nil && m.Replace != nil {
m = m.Replace
}
if m == nil || m.Version == "" {
return "(devel)"
}
return m.Version
}

@ -0,0 +1,60 @@
package datekeys
import (
"os"
"runtime/debug"
"strings"
"testing"
)
// SpecVersion names the specification of this repository: its file, its
// title and its record in spec/README.md.
func TestSpecVersionNamesTheSpecification(t *testing.T) {
spec, err := os.ReadFile("spec/DateKeys_Protocol_Specification_v" + SpecVersion + ".md")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(spec), "### Borrador normativo v"+SpecVersion+"\n") {
t.Error("the specification is not titled v" + SpecVersion)
}
readme, err := os.ReadFile("spec/README.md")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(readme), "draft v"+SpecVersion+" (") {
t.Error("spec/README.md does not record v" + SpecVersion)
}
}
func TestModulePath(t *testing.T) {
if modulePath != "g.activething.com/go/DateKeys" {
t.Fatalf("module path %q", modulePath)
}
}
func TestVersion(t *testing.T) {
if Version() == "" {
t.Fatal("empty version")
}
other := debug.Module{Path: "example.com/app", Version: "v1.2.3"}
for _, tc := range []struct {
name string
info debug.BuildInfo
want string
}{
{"the main module, tagged", debug.BuildInfo{Main: debug.Module{Path: modulePath, Version: "v0.1.0"}}, "v0.1.0"},
{"the main module, from a checkout", debug.BuildInfo{Main: debug.Module{Path: modulePath, Version: "v0.0.0-20260928125528-9ac9cd952f04"}},
"v0.0.0-20260928125528-9ac9cd952f04"},
{"the main module in a test", debug.BuildInfo{Main: debug.Module{Path: modulePath}}, "(devel)"},
{"a dependency", debug.BuildInfo{Main: other, Deps: []*debug.Module{{Path: modulePath, Version: "v0.2.0"}}}, "v0.2.0"},
{"replaced by another version", debug.BuildInfo{Main: other, Deps: []*debug.Module{
{Path: modulePath, Version: "v0.2.0", Replace: &debug.Module{Path: modulePath, Version: "v0.2.1"}}}}, "v0.2.1"},
{"replaced by a directory", debug.BuildInfo{Main: other, Deps: []*debug.Module{
{Path: modulePath, Version: "v0.2.0", Replace: &debug.Module{Path: "../datekeys-go"}}}}, "(devel)"},
{"not in the program", debug.BuildInfo{Main: other}, "(devel)"},
} {
if got := versionIn(&tc.info); got != tc.want {
t.Errorf("%s: %q, want %q", tc.name, got, tc.want)
}
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.