Skip to content

feat(bridge): add bridge and host microservice models - #3695

Open
ryanmelt wants to merge 1 commit into
pr1-alpine-to-debianfrom
pr2-bridge-models
Open

feat(bridge): add bridge and host microservice models#3695
ryanmelt wants to merge 1 commit into
pr1-alpine-to-debianfrom
pr2-bridge-models

Conversation

@ryanmelt

Copy link
Copy Markdown
Member

Stacked PR 2 of 5 — base: pr1-alpine-to-debian (#3694). Part of the #3688 split.

Pure additions — nothing references these yet. Ruby/Python bridge, host-microservice, and bridge-interface models, plus the iroh Python dependency (pyproject.toml / uv.lock).

Reviewer focus

  • Redis key schema
  • Ruby/Python parity between the model pairs
  • New dependency vetting (iroh)

Review after #3694.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpypi/​iroh@​1.0.0100100100100100

View full report

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
17.5% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.30%. Comparing base (4ed92c1) to head (c364862).

Additional details and impacted files
@@                   Coverage Diff                    @@
##           pr1-alpine-to-debian    #3695      +/-   ##
========================================================
- Coverage                 79.32%   79.30%   -0.03%     
========================================================
  Files                       885      885              
  Lines                     65367    65367              
  Branches                   2537     2537              
========================================================
- Hits                      51855    51838      -17     
- Misses                    12838    12856      +18     
+ Partials                    674      673       -1     
Flag Coverage Δ
frontend 63.59% <ø> (+0.07%) ⬆️
python 81.53% <ø> (-0.06%) ⬇️
ruby-api 82.24% <ø> (-0.30%) ⬇️
ruby-backend 84.07% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

"watchdog (>=6.0.0,<7.0.0)",
"websockets (>=16.0,<17.0)",
"pyserial>=3.5",
"iroh (>=1.0.0,<2.0.0)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread openc3/python/uv.lock
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]

[[package]]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

iroh ships four wheels and no sdist: macosx_11_0_arm64,
manylinux_2_28_aarch64, manylinux_2_28_x86_64, win_amd64. PyPI shows the
same set for 1.0.0 and 1.1.0.

That means uv sync hard-fails on Intel macOS (x86_64-apple-darwin) — no
wheel to install, no source to build. If Intel Mac is still a supported dev
platform, this needs an optional extra or an environment marker rather than an
unconditional core dependency.

The missing musl wheel is presumably why #3694 has to land first — worth saying
so explicitly in the PR body.

'public_key' => @public_key,
'ticket' => @ticket,
'app_public_key' => @app_public_key,
'enroll_code' => @enroll_code,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

enroll_code is serialized by as_json, which is what a ModelController or
API endpoint returns. That ships the pending one-time pairing secret with every
read of the bridge record, so anyone who can read it can redeem the enrollment.

The comment on line 27 says the bridge's private key "lives in the secrets
store, never here" — the enroll code deserves the same treatment, or at minimum
redaction from as_json with a separate accessor for the enroll path.

Same on the Python side at bridge_model.py:106.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed: enroll_code is returned in normal model JSON, exposing the pairing secret. Redact it and expose it only through enrollment.

# END NOTE

@classmethod
def from_json(cls, json_data: str | dict, scope: str):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

from_json and get_model here are byte-identical to the base Model
implementations at model.py:71-90, so they're dead code. Same in
bridge_interface_model.py:47 and host_microservice_model.py:50 — about 36
lines total.

Worth noting the existing overrides aren't precedent for this: scope_model.py
drops the scope argument and activity_model.py pops name/scope before
constructing, so both diverge from base for a reason. These three don't. Suggest
deleting all six.

self,
name: str,
scope: str,
bridge_name: str = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

bridge_name and stream default to None, but the Ruby twin requires both
as keyword args (host_microservice_model.rb:63). As written, Python can
persist a bridged interface with no bridge and no stream. Make them required to
match.

@needs_dependencies = needs_dependencies
end

def as_json(*a)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

as_json omits 'scope'. The Python twin includes it
(host_microservice_model.py:109), and BridgeModel includes it on both sides.

Round-trips work either way since both from_json implementations inject scope,
but the PR body claims identical JSON shape between the pairs, and this is the
one place it isn't.

@jmthomas jmthomas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Address all existing comments but otherwise LGTM.

require 'securerandom'

module OpenC3
# Stores the identity of a named Iroh bridge (the bridge_microservice hub).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does IROH mean?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Iroh is a Rust P2P networking stack, used in https://github.com/OpenC3/openc3-cosmos-app

# Generate a one-time manual-enrollment code, store it on this bridge, and
# return a token (base64 JSON of bridge name + hub ticket + code) that a
# remote openc3-app pastes to enroll. Requires the bridge to be up (ticket
# present). The code is redeemed once over the api/enroll ALPN.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does ALPN mean?

'public_key' => @public_key,
'ticket' => @ticket,
'app_public_key' => @app_public_key,
'enroll_code' => @enroll_code,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed: enroll_code is returned in normal model JSON, exposing the pairing secret. Redact it and expose it only through enrollment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants