From 1a7d9ee21050cf8a9adfe3f52a19a8b96b56f06c Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 29 Oct 2026 15:02:37 +0100 Subject: [PATCH 1/4] feat(orchestrator): log rejected payer funds and on-chain mode An underfunded payer was rejected with no server log, so a ticket face value above the claimable reserve looked like a client or signer fault. Log the payer, deposit, reserve and face value on rejection, and report on-chain mode on the start line. Co-Authored-By: Claude Sonnet 5.5 --- orchestrator/app.go | 2 +- orchestrator/payment_engine.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/orchestrator/app.go b/orchestrator/app.go index c066b7c..4f6f95f 100644 --- a/orchestrator/app.go +++ b/orchestrator/app.go @@ -363,7 +363,7 @@ func Serve(parent context.Context, p Params, logOut io.Writer) (result error) { var workers sync.WaitGroup workers.Go(func() { errs <- mainServer.Serve(mainListener) }) workers.Go(func() { errs <- metricsServer.Serve(metricsListener) }) - logger.Info("orchestrator started", "event", "started", "operation", "serve", "outcome", "success", "listen", p.Listen, "metrics_listen", p.MetricsListen) + logger.Info("orchestrator started", "event", "started", "operation", "serve", "outcome", "success", "listen", p.Listen, "metrics_listen", p.MetricsListen, "onchain", engine != nil) // Independent bounded workers keep slow RPC/health calls off the billing // and expiry paths. Every worker uses the shutdown context and is joined. startWorker := func(period, timeout time.Duration, work func(context.Context)) { diff --git a/orchestrator/payment_engine.go b/orchestrator/payment_engine.go index e60d6d7..0b01892 100644 --- a/orchestrator/payment_engine.go +++ b/orchestrator/payment_engine.go @@ -7,6 +7,7 @@ import ( "crypto/rand" "encoding/base64" "errors" + "log/slog" "math/big" "sync" "time" @@ -253,6 +254,7 @@ func (e *PaymentEngine) Receive(ctx context.Context, runner, manifest, paymentHe return ethcommon.Address{}, nil, paymentObservation{err, "rejected"} } if funds.Reserve.Cmp(params.FaceValue) < 0 || funds.Deposit.Cmp(params.FaceValue) < 0 { + slog.WarnContext(ctx, "orchestrator payer funds below ticket face value", "event", "payment_receive", "operation", "receive", "outcome", "rejected", "payer", payer, "deposit", funds.Deposit, "reserve", funds.Reserve, "face_value", params.FaceValue) return ethcommon.Address{}, nil, pm.ErrPayerUnavailable } e.mu.Lock() From e1280a4a2a82ca696c502c57c06a188f9218fbed Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 29 Oct 2026 15:02:38 +0100 Subject: [PATCH 2/4] feat(signer): log a startup line and use the text log handler The signer printed nothing when healthy. Log listen addresses at startup and use the same text handler as the orchestrator so both services share one log format. Co-Authored-By: Claude Sonnet 5.5 --- signer/app.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/signer/app.go b/signer/app.go index 6f9edc1..1be7191 100644 --- a/signer/app.go +++ b/signer/app.go @@ -5,11 +5,13 @@ import ( "errors" "fmt" "io" + "log/slog" "math/big" "net" "net/http" "net/netip" "net/url" + "os" "os/signal" "sync" "syscall" @@ -148,6 +150,7 @@ func (p Params) Validate() error { } func Serve(p Params) error { + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, nil))) ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer stop() return serve(ctx, p) @@ -270,6 +273,7 @@ func serve(parent context.Context, p Params) error { } workers.Go(func() { errCh <- srv.Serve(listener) }) workers.Go(func() { errCh <- metricsServer.Serve(metricsListener) }) + slog.Info("signer started", "component", "signer", "event", "started", "operation", "serve", "outcome", "success", "listen", p.Listen, "metrics_listen", p.MetricsListen) select { case <-ctx.Done(): case err = <-errCh: From 6983cd3b47d4d6ec66e914d6c67fd8d2d3d2455e Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 29 Oct 2026 15:09:56 +0100 Subject: [PATCH 3/4] fix(orchestrator): route the funds warning through the engine logger The warning used the default slog handler, so it printed in a different format from the other orchestrator lines. Use the engine's store logger, as the payment observed line does. Co-Authored-By: Claude Sonnet 5.5 --- orchestrator/payment_engine.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/orchestrator/payment_engine.go b/orchestrator/payment_engine.go index 0b01892..7d0c94d 100644 --- a/orchestrator/payment_engine.go +++ b/orchestrator/payment_engine.go @@ -7,7 +7,6 @@ import ( "crypto/rand" "encoding/base64" "errors" - "log/slog" "math/big" "sync" "time" @@ -254,7 +253,9 @@ func (e *PaymentEngine) Receive(ctx context.Context, runner, manifest, paymentHe return ethcommon.Address{}, nil, paymentObservation{err, "rejected"} } if funds.Reserve.Cmp(params.FaceValue) < 0 || funds.Deposit.Cmp(params.FaceValue) < 0 { - slog.WarnContext(ctx, "orchestrator payer funds below ticket face value", "event", "payment_receive", "operation", "receive", "outcome", "rejected", "payer", payer, "deposit", funds.Deposit, "reserve", funds.Reserve, "face_value", params.FaceValue) + if e.store != nil && e.store.logger != nil { + e.store.logger.WarnContext(ctx, "orchestrator payer funds below ticket face value", "event", "payment", "operation", "receive", "outcome", "rejected", "payer", payer, "deposit", funds.Deposit, "reserve", funds.Reserve, "face_value", params.FaceValue) + } return ethcommon.Address{}, nil, pm.ErrPayerUnavailable } e.mu.Lock() From 325136bfbc2b1a6a64a860c3f87db1b059eb4f7a Mon Sep 17 00:00:00 2001 From: Rick Staa Date: Thu, 29 Oct 2026 15:15:11 +0100 Subject: [PATCH 4/4] fix(orchestrator): name the claimable reserve in the funds warning The logged reserve is the amount claimable by this orchestrator, not the payer's total reserve. Say so, to avoid mistaking a funded wallet for a logging error. Co-Authored-By: Claude Sonnet 5.5 --- orchestrator/payment_engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestrator/payment_engine.go b/orchestrator/payment_engine.go index 7d0c94d..34e713b 100644 --- a/orchestrator/payment_engine.go +++ b/orchestrator/payment_engine.go @@ -254,7 +254,7 @@ func (e *PaymentEngine) Receive(ctx context.Context, runner, manifest, paymentHe } if funds.Reserve.Cmp(params.FaceValue) < 0 || funds.Deposit.Cmp(params.FaceValue) < 0 { if e.store != nil && e.store.logger != nil { - e.store.logger.WarnContext(ctx, "orchestrator payer funds below ticket face value", "event", "payment", "operation", "receive", "outcome", "rejected", "payer", payer, "deposit", funds.Deposit, "reserve", funds.Reserve, "face_value", params.FaceValue) + e.store.logger.WarnContext(ctx, "orchestrator payer deposit or claimable reserve below ticket face value", "event", "payment", "operation", "receive", "outcome", "rejected", "payer", payer, "deposit", funds.Deposit, "claimable_reserve", funds.Reserve, "face_value", params.FaceValue) } return ethcommon.Address{}, nil, pm.ErrPayerUnavailable }