aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-28 04:52:17 -0800
committerFuwn <[email protected]>2026-02-28 04:52:17 -0800
commitbb02fa43e5776e3d7b95cb47f0594eb4bd7f907f (patch)
tree9d2e2f49c2e0cb9e153ca266d10b9b610e38c771
parentbuild(task): add multi-arch docker build targets (diff)
downloadplutia-test-bb02fa43e5776e3d7b95cb47f0594eb4bd7f907f.tar.xz
plutia-test-bb02fa43e5776e3d7b95cb47f0594eb4bd7f907f.zip
docs: refine user-facing wording for clarity and consistency
-rw-r--r--README.md6
-rw-r--r--Taskfile.yaml28
-rw-r--r--cmd/plutia/main.go16
-rw-r--r--internal/api/server.go28
-rw-r--r--internal/config/config.go10
5 files changed, 44 insertions, 44 deletions
diff --git a/README.md b/README.md
index 360b11f..edff3db 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@ Plutia is a deterministic, verifiable PLC mirror for `plc.directory` with signed
- Deterministic canonical operation handling and signature-chain verification.
- Signed Merkle checkpoints and DID inclusion proof API.
- Corruption detection and restart-safe ingestion.
-- High-density storage scaling around ~1.2KB/op in benchmarked runs.
-- ~45x faster than naive replay in benchmarked runs.
+- Measured storage in benchmarked runs is about 1.2 KB per operation.
+- Benchmarked replay throughput is about 45x higher than naive replay in the same test setup.
## Trust Model
@@ -54,7 +54,7 @@ docker build \
-t plutia:local .
```
-In a second terminal, generate the mirror signing key inside the running container:
+In another terminal, generate the mirror signing key inside the running container:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec plutia /app/plutia keygen --out=/var/lib/plutia/mirror.key
diff --git a/Taskfile.yaml b/Taskfile.yaml
index 9d6f6a7..585f3b0 100644
--- a/Taskfile.yaml
+++ b/Taskfile.yaml
@@ -5,14 +5,14 @@ vars:
VERSION:
sh: |
if [ ! -f VERSION ]; then
- echo "VERSION file is required" >&2
+ echo "VERSION file is required. Create VERSION and run the task again." >&2
exit 1
fi
tr -d '\r\n' < VERSION
VERSION_TAG:
sh: |
if [ ! -f VERSION ]; then
- echo "VERSION file is required" >&2
+ echo "VERSION file is required. Create VERSION and run the task again." >&2
exit 1
fi
v="$(tr -d '\r\n' < VERSION)"
@@ -20,11 +20,11 @@ vars:
COMMIT:
sh: |
if ! command -v git >/dev/null 2>&1; then
- echo "git is required to compute build metadata" >&2
+ echo "Git is required to compute build metadata." >&2
exit 1
fi
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
- echo "git repository context is required" >&2
+ echo "A Git repository context is required." >&2
exit 1
fi
git rev-parse --short HEAD
@@ -44,7 +44,7 @@ tasks:
desc: Ensure git metadata is available for version injection.
preconditions:
- sh: command -v git >/dev/null 2>&1
- msg: git is required but was not found in PATH
+ msg: Git is required. Install Git and run the task again.
- sh: git rev-parse --is-inside-work-tree >/dev/null 2>&1
msg: task must run from inside the plutia git repository
cmds:
@@ -124,7 +124,7 @@ tasks:
if command -v staticcheck >/dev/null 2>&1; then
staticcheck ./...
else
- echo "staticcheck not installed; skipping"
+ echo "staticcheck is not installed, so this step is skipped."
fi
verify:small:
@@ -147,7 +147,7 @@ tasks:
- |
checkpoint_count="$(find ./tmp-test-data/checkpoints -maxdepth 1 -type f -name '*.json' ! -name '*.state.json' | wc -l | awk '{print $1}')"
if [ "${checkpoint_count}" -lt 1 ]; then
- echo "verify:small failed: no checkpoint files generated" >&2
+ echo "verify:small failed: no checkpoint files were generated." >&2
exit 1
fi
global_seq="$(awk '/replay complete at sequence/ {print $5}' ./tmp-test-data/verify-small.log | tail -n 1)"
@@ -208,7 +208,7 @@ tasks:
- |
echo "verify:1m checkpoint timing metrics"
if ! grep -q 'checkpoint_metrics' ./scale-test-data-1m/verify-1m.log; then
- echo "verify:1m failed: no checkpoint metrics emitted" >&2
+ echo "verify:1m failed: no checkpoint metrics were emitted." >&2
exit 1
fi
grep 'checkpoint_metrics' ./scale-test-data-1m/verify-1m.log
@@ -228,7 +228,7 @@ tasks:
- |
block_files="$(find ./scale-test-data-resolver/ops -maxdepth 1 -type f -name '*.zst' | wc -l | awk '{print $1}')"
if [ "${block_files}" -ne 0 ]; then
- echo "verify:resolver failed: expected zero block files, found ${block_files}" >&2
+ echo "verify:resolver failed: expected zero block files, found ${block_files}." >&2
exit 1
fi
echo "verify:resolver summary"
@@ -258,17 +258,17 @@ tasks:
- |
block_file="$(find ./tmp-test-data-corruption/ops -maxdepth 1 -type f -name '*.zst' | sort | head -n 1)"
if [ -z "${block_file}" ]; then
- echo "verify:corruption failed: no block file was created" >&2
+ echo "verify:corruption failed: no block file was created." >&2
exit 1
fi
printf 'tamper' >> "${block_file}"
- |
if {{.BIN}} replay --config=config.default.yaml --max-ops=5000 > ./tmp-test-data-corruption/restart.log 2>&1; then
- echo "verify:corruption failed: replay succeeded after tampering" >&2
+ echo "verify:corruption failed: replay succeeded after tampering." >&2
exit 1
fi
if ! grep -qi 'corruption' ./tmp-test-data-corruption/restart.log; then
- echo "verify:corruption failed: corruption was not reported" >&2
+ echo "verify:corruption failed: corruption was not reported." >&2
cat ./tmp-test-data-corruption/restart.log
exit 1
fi
@@ -330,8 +330,8 @@ tasks:
- sh: docker buildx version >/dev/null 2>&1
msg: docker buildx is required for multi-architecture docker:build
cmds:
- - docker buildx build --platform linux/amd64 -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}}-amd64 --load .
- - docker buildx build --platform linux/arm64 -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}}-arm64 --load .
+ - docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/fuwn/plutia:{{.VERSION_TAG}} --push .
+ - docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/fuwn/plutia:latest --push .
docker:run:
desc: Run Plutia container with mounted config and persistent data.
diff --git a/cmd/plutia/main.go b/cmd/plutia/main.go
index b6192f5..a3e8567 100644
--- a/cmd/plutia/main.go
+++ b/cmd/plutia/main.go
@@ -88,7 +88,7 @@ func main() {
func runServe(args []string) error {
fs := flag.NewFlagSet("serve", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
maxOps := fs.Uint64("max-ops", 0, "max operations to ingest in this process (0 = unlimited)")
if err := fs.Parse(args); err != nil {
@@ -175,7 +175,7 @@ func runServe(args []string) error {
func runReplay(args []string) error {
fs := flag.NewFlagSet("replay", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
maxOps := fs.Uint64("max-ops", 0, "max operations to ingest in this process (0 = unlimited)")
if err := fs.Parse(args); err != nil {
@@ -212,8 +212,8 @@ func runReplay(args []string) error {
func runVerify(args []string) error {
fs := flag.NewFlagSet("verify", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
- did := fs.String("did", "", "did to verify")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
+ did := fs.String("did", "", "DID to verify")
if err := fs.Parse(args); err != nil {
return err
@@ -243,7 +243,7 @@ func runVerify(args []string) error {
func runSnapshot(args []string) error {
fs := flag.NewFlagSet("snapshot", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
if err := fs.Parse(args); err != nil {
return err
@@ -271,7 +271,7 @@ func runSnapshot(args []string) error {
func runBench(args []string) error {
fs := flag.NewFlagSet("bench", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
maxOps := fs.Uint64("max-ops", 200000, "max operations to ingest for benchmark")
interval := fs.Duration("interval", 10*time.Second, "rolling report interval")
@@ -365,8 +365,8 @@ func runBench(args []string) error {
func runCompare(args []string) error {
fs := flag.NewFlagSet("compare", flag.ExitOnError)
- configPath := fs.String("config", "config.default.yaml", "config path")
- remote := fs.String("remote", "", "remote mirror base URL")
+ configPath := fs.String("config", "config.default.yaml", "path to config file")
+ remote := fs.String("remote", "", "base URL for remote mirror")
if err := fs.Parse(args); err != nil {
return err
diff --git a/internal/api/server.go b/internal/api/server.go
index a3b4513..158a6fe 100644
--- a/internal/api/server.go
+++ b/internal/api/server.go
@@ -137,7 +137,7 @@ func (s *Server) handleLatestCheckpoint(w http.ResponseWriter, r *http.Request)
}
if !ok {
- writeErr(w, http.StatusNotFound, fmt.Errorf("no checkpoint available"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("No checkpoint is available."))
return
}
@@ -149,7 +149,7 @@ func (s *Server) handleCheckpointBySequence(w http.ResponseWriter, r *http.Reque
path := strings.TrimPrefix(r.URL.Path, "/checkpoints/")
if path == "" {
- writeErr(w, http.StatusNotFound, fmt.Errorf("missing checkpoint sequence in request path"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("Missing checkpoint sequence in request path."))
return
}
@@ -157,7 +157,7 @@ func (s *Server) handleCheckpointBySequence(w http.ResponseWriter, r *http.Reque
seq, err := strconv.ParseUint(path, 10, 64)
if err != nil {
- writeErr(w, http.StatusBadRequest, fmt.Errorf("invalid checkpoint sequence"))
+ writeErr(w, http.StatusBadRequest, fmt.Errorf("Invalid checkpoint sequence."))
return
}
@@ -171,7 +171,7 @@ func (s *Server) handleCheckpointBySequence(w http.ResponseWriter, r *http.Reque
}
if !ok {
- writeErr(w, http.StatusNotFound, fmt.Errorf("checkpoint not found"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("Checkpoint not found."))
return
}
@@ -183,7 +183,7 @@ func (s *Server) handleDID(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/did/")
if path == "" {
- writeErr(w, http.StatusBadRequest, fmt.Errorf("missing DID in request path"))
+ writeErr(w, http.StatusBadRequest, fmt.Errorf("Missing DID in request path."))
return
}
@@ -192,7 +192,7 @@ func (s *Server) handleDID(w http.ResponseWriter, r *http.Request) {
did := strings.TrimSuffix(path, "/proof")
if !s.allowRequest(r, limiterProof) {
- writeErr(w, http.StatusTooManyRequests, fmt.Errorf("too many proof requests. Try again later."))
+ writeErr(w, http.StatusTooManyRequests, fmt.Errorf("Too many proof requests. Try again later."))
return
}
@@ -203,7 +203,7 @@ func (s *Server) handleDID(w http.ResponseWriter, r *http.Request) {
}
if !s.allowRequest(r, limiterResolve) {
- writeErr(w, http.StatusTooManyRequests, fmt.Errorf("too many resolve requests. Try again later."))
+ writeErr(w, http.StatusTooManyRequests, fmt.Errorf("Too many resolve requests. Try again later."))
return
}
@@ -217,7 +217,7 @@ func (s *Server) handleDIDResolve(w http.ResponseWriter, r *http.Request, did st
resolvedState, err := s.ingestor.ResolveState(r.Context(), did)
if err != nil {
if errors.Is(err, ingest.ErrDIDNotFound) {
- writeErr(w, http.StatusNotFound, fmt.Errorf("DID not found"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("DID not found."))
return
}
@@ -243,7 +243,7 @@ func (s *Server) handleDIDResolve(w http.ResponseWriter, r *http.Request, did st
}
if !ok {
- writeErr(w, http.StatusNotFound, fmt.Errorf("DID not found"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("DID not found."))
return
}
@@ -277,13 +277,13 @@ func (s *Server) handleDIDResolve(w http.ResponseWriter, r *http.Request, did st
func (s *Server) handleDIDProof(w http.ResponseWriter, r *http.Request, did string) {
if s.cfg.Mode == config.ModeThin {
- writeErr(w, http.StatusNotImplemented, fmt.Errorf("thin mode does not support checkpoint proofs"))
+ writeErr(w, http.StatusNotImplemented, fmt.Errorf("Thin mode does not support checkpoint proofs."))
return
}
if s.ingestor == nil {
- writeErr(w, http.StatusServiceUnavailable, fmt.Errorf("service unavailable"))
+ writeErr(w, http.StatusServiceUnavailable, fmt.Errorf("Service unavailable."))
return
}
@@ -331,7 +331,7 @@ func (s *Server) handleDIDProof(w http.ResponseWriter, r *http.Request, did stri
}
if !found {
- writeErr(w, http.StatusNotFound, fmt.Errorf("DID is not present in the checkpoint state"))
+ writeErr(w, http.StatusNotFound, fmt.Errorf("DID is not present in the checkpoint state."))
return
}
@@ -1016,7 +1016,7 @@ func (s *Server) selectCheckpointForProof(r *http.Request) (types.CheckpointV1,
}
if !ok {
- return types.CheckpointV1{}, nil, fmt.Errorf("no checkpoint available")
+ return types.CheckpointV1{}, nil, fmt.Errorf("No checkpoint is available.")
}
return cp, func() error {
@@ -1041,7 +1041,7 @@ func (s *Server) selectCheckpointForProof(r *http.Request) (types.CheckpointV1,
seq, err := strconv.ParseUint(checkpointParam, 10, 64)
if err != nil {
- return types.CheckpointV1{}, nil, fmt.Errorf("invalid checkpoint query parameter")
+ return types.CheckpointV1{}, nil, fmt.Errorf("Invalid checkpoint query parameter.")
}
cp, ok, err := s.store.GetCheckpoint(seq)
diff --git a/internal/config/config.go b/internal/config/config.go
index e94b5ea..69173fd 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -176,21 +176,21 @@ func applyEnv(cfg *Config) {
func (c Config) Validate() error {
if c.Mode != ModeResolver && c.Mode != ModeMirror && c.Mode != ModeThin {
- return fmt.Errorf("invalid mode %q", c.Mode)
+ return fmt.Errorf("mode must be one of mirror, resolver, or thin (got %q)", c.Mode)
}
switch c.VerifyPolicy {
case VerifyFull, VerifyLazy, VerifyStateOnly:
default:
- return fmt.Errorf("invalid verify policy %q", c.VerifyPolicy)
+ return fmt.Errorf("verify must be one of full, lazy, or state-only (got %q)", c.VerifyPolicy)
}
if c.DataDir == "" {
- return errors.New("data_dir is required")
+ return errors.New("set data_dir in the config file")
}
if c.PLCSource == "" {
- return errors.New("plc_source is required")
+ return errors.New("set plc_source in the config file")
}
if c.ZstdLevel < 1 || c.ZstdLevel > 22 {
@@ -226,7 +226,7 @@ func (c Config) Validate() error {
}
if c.ListenAddr == "" {
- return errors.New("listen_addr is required")
+ return errors.New("set listen_addr in the config file")
}
if c.PollInterval <= 0 {