Skip to content

Add remote signer endpoint for GetOrchestratorInfo#3791

Merged
j0sh merged 15 commits intomasterfrom
ja/remote-signer-orchinfo
Jan 26, 2026
Merged

Add remote signer endpoint for GetOrchestratorInfo#3791
j0sh merged 15 commits intomasterfrom
ja/remote-signer-orchinfo

Conversation

@j0sh
Copy link
Collaborator

@j0sh j0sh commented Oct 22, 2025

This PR is the first part of the remote signing feature, implementing the GetOrchestratorInfo request. See the design background for additional motivation and design detail around remote signers.

Remote signing support for Live AI (live-video-to-video) consists of two parts:

This PR adds a new mode to go-livepeer: -remoteSigner.

The remote signer exposes a HTTP POST endpoint at /sign-orchestrator-info. It currently does only does one thing: produces a signature for use in the OrchestratorInfo gRPC call. The response includes the signer's Ethereum address as well as the signature itself.

Like the other types of mode flags (gateway, orchestrator, redeemer, etc) the remote signer cannot be combined with other modes.

The gateway adds a new -remoteSignerAddr flag which specifies the base address of the remote signer to use (host:port). When configured, the gateway pre-fetches the remote signature for OrchestratorInfo at start-up time and caches it, so subsequent calls to OrchestratorInfo do not have to incur additional remote calls.

One might note that this OrchestratorInfo signing scheme doesn't actually accomplish much at all: the signature effectively static, not scoped to any one orchestrator, and never expires. This is a legacy aspect of the OrchestratorInfo flow that we have to accommodate for now; it is a vestigial corner of the codebase, but one that we've judged internally to be harmless.

@github-actions github-actions bot added the go Pull requests that update Go code label Oct 22, 2025
@codecov
Copy link

codecov bot commented Oct 22, 2025

Codecov Report

❌ Patch coverage is 5.30973% with 107 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.15842%. Comparing base (1a26807) to head (e1bb0a9).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
server/remote_signer.go 0.00000% 63 Missing ⚠️
cmd/livepeer/starter/starter.go 13.15789% 33 Missing ⚠️
core/broadcaster.go 0.00000% 9 Missing ⚠️
cmd/livepeer/starter/flags.go 0.00000% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                 Coverage Diff                 @@
##              master       #3791         +/-   ##
===================================================
- Coverage   32.24436%   32.15842%   -0.08594%     
===================================================
  Files            169         170          +1     
  Lines          41201       41308        +107     
===================================================
- Hits           13285       13284          -1     
- Misses         26915       27023        +108     
  Partials        1001        1001                 
Files with missing lines Coverage Δ
common/types.go 0.00000% <ø> (ø)
core/livepeernode.go 63.08725% <ø> (ø)
server/rpc.go 69.13580% <100.00000%> (-0.28316%) ⬇️
cmd/livepeer/starter/flags.go 0.00000% <0.00000%> (ø)
core/broadcaster.go 0.00000% <0.00000%> (ø)
cmd/livepeer/starter/starter.go 22.49474% <13.15789%> (-0.31102%) ⬇️
server/remote_signer.go 0.00000% <0.00000%> (ø)

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1a26807...e1bb0a9. Read the comment docs.

Files with missing lines Coverage Δ
common/types.go 0.00000% <ø> (ø)
core/livepeernode.go 63.08725% <ø> (ø)
server/rpc.go 69.13580% <100.00000%> (-0.28316%) ⬇️
cmd/livepeer/starter/flags.go 0.00000% <0.00000%> (ø)
core/broadcaster.go 0.00000% <0.00000%> (ø)
cmd/livepeer/starter/starter.go 22.49474% <13.15789%> (-0.31102%) ⬇️
server/remote_signer.go 0.00000% <0.00000%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@ad-astra-video ad-astra-video left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial thoughts as I went through this PR:

  • We should add an Authorization secret to protect the remote signer endpoints. WDYT?

  • I think we should add a standard port at top of starter.go similar to other modes for go-livepeer

  • I think the signed Gateway address to send in GetOrchestrator request should be a GET request since no payload is required. We probably should cache this on the Gateway as well since it doesn't change and would lower the requests to the signer service.

    • Seems like this request should be sent at Gateway startup and the Gateway should fail to startup if the request fails.
  • Need a POST request endpoint on remote signer expecting json payload of { nonces: [list of nonces to use], ticket_params: [orch-ticket-params] that returns list of "tickets" which is just a list of TicketSenderParams that is nonce and signature for all requested nonces.

  • Where should the Sender and Sender Monitor live? I think the Gateway (or local SDK) should still be responsible for this since it is directly communicating with the Orchestrator. Keeping the remote signer lightweight will assist in payment services being built since it does one thing which is use a ethereum address to sign a specific payload.

  • When we settle on the implementation we should add a doc/remote_signer.md for basic instructions to run Remote Signer.

@j0sh
Copy link
Collaborator Author

j0sh commented Nov 13, 2025

We should add an Authorization secret to protect the remote signer endpoints.

Auth will absolutely be needed for clearinghouses. A header in conjunction with webhooks is probably the way to go. But I would defer implementing auth for now until the clearinghouse requirements are clearer. Our other endpoints are unauthenticated and generally sit behind a layer of proxies, so they should never be exposed to the world. This specific mode is also opt-in via the -remoteSigner flag and few operators will actually use it in prod, so the risk should be low for now.

I think we should add a standard port at top of starter.go similar to other modes for go-livepeer

Not sure if that is necessary since you can still set the port that the remote signer uses with the -httpAddr flag, the same way we set the port for the other modes. Is there an issue with the current default port?

I think the signed Gateway address to send in GetOrchestrator request should be a GET request since no payload is required

We’ll probably need payloads soon enough, since GetOrchestrator requests are parameterized (capabilities, etc). I just haven’t thought through that mapping yet, and will probably punt on that until after this PR.

Need a POST request endpoint on remote signer expecting json payload ...

Yes, that would be for ticket signing which isn't part of this PR.

Where should the Sender and Sender Monitor live? I think the Gateway (or local SDK) should still be responsible for this since it is directly communicating with the Orchestrator.  Keeping the remote signer lightweight will assist in payment services being built since it does one thing which is use a ethereum address to sign a specific payload.

My thinking is actually the opposite. There will probably be only one remote signer implementation - this one - but many SDK implementations. Livepeer payments are extremely complicated, and we don't want to require a Javascript or Python developer to wade through that. So we should handle as much in the remote signer as possible, and keep SDKs straightforward.


// Calls the remote signer service to get a signature for GetOrchInfo
func GetOrchInfoSig(remoteSignerHost *url.URL) (*OrchInfoSigResponse, error) {

Copy link
Member

@rickstaa rickstaa Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small non important formatting improvement.

Suggested change

Signature HexBytes `json:"signature"`
}

// Calls the remote signer service to get a signature for GetOrchInfo
Copy link
Member

@rickstaa rickstaa Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@j0sh another small maintainability improvement making it directly clear this is a gateway helper and not Signer method.

Suggested change
// Calls the remote signer service to get a signature for GetOrchInfo
// GetOrchInfoSig is a gateway helper that calls the remote signer to get the GetOrchInfo signature

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tweaked in 950527c

@rickstaa
Copy link
Member

rickstaa commented Jan 5, 2026

@j0sh this looks very clean. I haven’t tested it yet, I was planning to do so once #3822 is ready, but no blockers from my side to approve this PR when its out of draft.

@rickstaa
Copy link
Member

rickstaa commented Jan 5, 2026

My thinking is actually the opposite. There will probably be only one remote signer implementation - this one - but many SDK implementations. Livepeer payments are extremely complicated, and we don't want to require a Javascript or Python developer to wade through that. So we should handle as much in the remote signer as possible, and keep SDKs straightforward.

I may be overlooking some details since I’m less in the weeds than the two of you, but I agree with @j0sh on this: it’s best to keep all payment logic centralized in the new node and outside the gateway and orchestration layers. This allows the node to better live up to the vision outlined in the payment clearinghouse spec, where it significantly simplifies the stack and enables lightweight gateways.

The main caveat is that the payment logic needs to be general enough to support multiple job types. I haven’t fully thought this through yet, but a reasonable starting point would be to further decouple it from live-to-live over time. Instead of pricing in pixels, we could move toward compute units, with the orchestrator defining compute-per-second and the signer calculating the final cost. I know you’ve already discussed aspects of this.

Additionally, we should ensure that state management, or a well-defined stateless communication model, as discussed by Josh in the tech spec, is implemented in a way that allows all parties to dispute transactions if needed. This requirement likely only becomes relevant once stronger compute verification is in place, potentially in 2026 (see this roadmap item).

Copy link
Member

@rickstaa rickstaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The technical implementation of this pull request makes sense to me. It can be merged once you and @ad-astra-video are satisfied with it and it has been tested.

// flags
cfg.TestOrchAvail = fs.Bool("startupAvailabilityCheck", *cfg.TestOrchAvail, "Set to false to disable the startup Orchestrator availability check on the configured serviceAddr")
cfg.RemoteSigner = fs.Bool("remoteSigner", *cfg.RemoteSigner, "Set to true to run remote signer service")
cfg.RemoteSignerAddr = fs.String("remoteSignerAddr", *cfg.RemoteSignerAddr, "URL of remote signer service to use (e.g., http://localhost:8935). Gateway only.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wdyt about renaming RemoteSignerAddr to RemoteSignerUrl? It might help prevent users from confusing as a remote signer flag for binding to interface/port (which -httpAddr is used for).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good idea. We use both (eg, -orchAddr) but agree that the url suffix is better here. ab87e74

@j0sh j0sh force-pushed the ja/remote-signer-orchinfo branch from 210bcbf to e958057 Compare January 23, 2026 00:37
@j0sh j0sh marked this pull request as ready for review January 23, 2026 00:38
@j0sh j0sh enabled auto-merge (squash) January 26, 2026 17:22
@j0sh j0sh merged commit ab55f82 into master Jan 26, 2026
18 checks passed
@j0sh j0sh deleted the ja/remote-signer-orchinfo branch January 26, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants