mirror of
https://github.com/luxfi/esign.git
synced 2026-10-30 16:58:18 +00:00
Make Hanzo Base — per-tenant SQLite + extension runtime (HIP-0105,
HIP-0107, HIP-0302) — the canonical production backend. MemStore stays
for tests and ephemeral local dev.
* pkg/basestore — BaseStore implements api.Store on a core.App.
Single esign_envelopes collection holds the canonical JSON
envelope as the body field; state + doc_hash + version +
updated_at are mirrored as indexed columns for cheap dashboard
queries, but the JSON blob is the source of truth so envelope
schema changes never require a SQLite migration.
ensureCollection is idempotent.
Indexes: (state, updated_at DESC), (doc_hash).
* pkg/basestore/store_test.go — round-trip, upsert, missing-id,
empty-id rejection. Uses t.TempDir() so each test isolates its
own SQLite file.
* pkg/api/handlers.go — bump default envelope ID generator from
"env_<8 hex>" to "esign<16 hex>" (21 chars, ^[a-z0-9]+$) to
match Base's default record-id format and clear the 15-char
minimum. ID stays deterministic from the document so identical
bytes get the same envelope ID.
* cmd/esignd — default to BaseStore against `--data-dir esign_data`.
Pass `--memory` for the in-process MemStore (ephemeral, tests).
buildStore handles bootstrap + cleanup so the rest of main stays
a clean wire-up.
Test summary: 4 packages, 26 tests, all green.
102 lines
3.0 KiB
Go
102 lines
3.0 KiB
Go
// Copyright 2026 Lux Industries Inc. and the luxfi/esign authors.
|
|
// Licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
|
|
// esignd is the standalone luxfi/esign daemon. It serves the eSign RPC
|
|
// surface over native ZAP on the configured listen address; downstream
|
|
// services (hanzoai/esign, liquidity/bd, etc.) connect via ZAP wire and
|
|
// drive the envelope state machine.
|
|
//
|
|
// Storage default is Hanzo Base — per-tenant SQLite + extension runtime —
|
|
// so an esignd binary running anywhere in the Hanzo cloud composes the
|
|
// same persistence substrate that backs every other Go service. Pass
|
|
// `--memory` for the in-process MemStore (tests, CI, ephemeral local
|
|
// dev). Pass `--data-dir` to override Base's default SQLite location.
|
|
//
|
|
// Flags:
|
|
//
|
|
// --zap ZAP listen address (default ":9650")
|
|
// --data-dir Base SQLite data directory (default ./esign_data)
|
|
// --memory use in-memory store (overrides --data-dir; not durable)
|
|
//
|
|
// Logging goes to stderr in plain-text key=value pairs so it's both
|
|
// human-readable and grep-friendly.
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/hanzoai/base"
|
|
|
|
"github.com/luxfi/esign/pkg/api"
|
|
"github.com/luxfi/esign/pkg/basestore"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
zapAddr string
|
|
dataDir string
|
|
useMem bool
|
|
)
|
|
flag.StringVar(&zapAddr, "zap", ":9650", "ZAP listen address")
|
|
flag.StringVar(&dataDir, "data-dir", "./esign_data", "Base SQLite data directory")
|
|
flag.BoolVar(&useMem, "memory", false, "use in-memory store (overrides --data-dir)")
|
|
flag.Parse()
|
|
|
|
logger := log.New(os.Stderr, "esignd: ", log.LstdFlags|log.Lmicroseconds)
|
|
|
|
store, cleanup, err := buildStore(useMem, dataDir, logger)
|
|
if err != nil {
|
|
logger.Fatalf("store: %v", err)
|
|
}
|
|
defer cleanup()
|
|
|
|
srv, err := api.NewZAPServer(zapAddr, api.New(store))
|
|
if err != nil {
|
|
logger.Fatalf("bind %s: %v", zapAddr, err)
|
|
}
|
|
logger.Printf("zap listening on %s", srv.Addr())
|
|
|
|
done := make(chan struct{})
|
|
go func() {
|
|
if err := srv.Serve(); err != nil {
|
|
logger.Printf("serve: %v", err)
|
|
}
|
|
close(done)
|
|
}()
|
|
|
|
stop := make(chan os.Signal, 1)
|
|
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
|
|
<-stop
|
|
logger.Printf("shutdown requested")
|
|
_ = srv.Close()
|
|
<-done
|
|
logger.Printf("clean exit")
|
|
}
|
|
|
|
// buildStore picks the persistence layer. MemStore for transient runs;
|
|
// Hanzo Base (per-tenant SQLite) for everything else. Returns a cleanup
|
|
// closure so the caller can defer Base shutdown without leaking it
|
|
// through every error path.
|
|
func buildStore(useMem bool, dataDir string, logger *log.Logger) (api.Store, func(), error) {
|
|
if useMem {
|
|
logger.Printf("store: in-memory (transient)")
|
|
return api.NewMemStore(), func() {}, nil
|
|
}
|
|
app := base.NewWithConfig(base.Config{DefaultDataDir: dataDir})
|
|
if err := app.Bootstrap(); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
store, err := basestore.NewBaseStore(app)
|
|
if err != nil {
|
|
_ = app.ResetBootstrapState()
|
|
return nil, nil, err
|
|
}
|
|
logger.Printf("store: base (data-dir=%s)", dataDir)
|
|
cleanup := func() { _ = app.ResetBootstrapState() }
|
|
return store, cleanup, nil
|
|
}
|