Skip to content

feat(auth): allow non-admin user role to use HTTP sink/IO nodes#634

Merged
streamer45 merged 8 commits into
mainfrom
devin/1782566765-user-role-http-nodes
Jun 28, 2026
Merged

feat(auth): allow non-admin user role to use HTTP sink/IO nodes#634
streamer45 merged 8 commits into
mainfrom
devin/1782566765-user-role-http-nodes

Conversation

@staging-devin-ai-integration

@staging-devin-ai-integration staging-devin-ai-integration Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • The built-in user role's allowed_nodes excluded transport::http::* entirely, so HTTP-transport gateways (speech-gateway, the upcoming web-capture from feat(examples): add web-capture gateway #631) were forced to use an admin token. Allow transport::http::mse (serves over the caller's own request) while keeping transport::http::fetcher denied (arbitrary-URL fetch / SSRF) — hence a single node, not the transport::http::* wildcard.
  • The oneshot streamkit::http_input/http_output markers are not gated by allowed_nodes (the oneshot path treats them as implicitly allowed at every is_node_allowed call site), so they are intentionally not listed — adding them was dead config and is removed here.
  • Ship a least-privilege gateway role (authorization guide + samples/skit.toml) that can actually build the web-capture pipeline (servo → pixel_convert → vp9::encoder → webm::muxer → mse). Note: a plugin kind must be in both allowed_nodes and allowed_plugins, because is_node_allowed() runs before the plugin check.
  • Align the sample [permissions.roles.user] with Permissions::user(): add the *_samples flags (without which allowed_samples is unreachable), add the safe core::param_bridge, drop demo/*, and replace the broad core::*/transport::* wildcards with the explicit safe set.

Review & Validation

  • Confirm the safe/risky split holds for both roles: mse allowed; fetcher, core::file_writer, core::object_store_writer denied.
  • Behavior change: the tightened sample user role can no longer run tts_to_s3 / transcode_to_s3 / moq_s3_archive (object_store_writer) or moq_to_rtmp_composite (rtmp::publish) — matching the built-in default; these stay runnable under admin.
  • cargo test -p streamkit-server permissions and --test sample_config_test.

Notes

  • examples/web-capture doesn't exist yet (feat(examples): add web-capture gateway #631 in progress), so there's no example token to update; the servo plugin referenced by the gateway example is real (plugins/native/servo, used by samples/pipelines/oneshot/web_capture.yml).

Link to Devin session: https://staging.itsdev.in/sessions/88d8f7ee72f640cf8c080c08fbd46667
Requested by: @streamer45


Devin Review

Status Commit
🟢 Reviewed 4f8ebd0
Open in Devin Review (Staging)

The built-in user role excluded transport::http::* entirely and all
streamkit::* nodes, forcing HTTP-transport gateways to run with an admin
token. Allow the safe sink/IO nodes (transport::http::mse,
streamkit::http_input, streamkit::http_output) while keeping
transport::http::fetcher denied (SSRF risk), and document a
least-privilege gateway role.

Closes #632

Signed-off-by: streamkit-devin <devin@streamkit.dev>
@streamer45 streamer45 self-assigned this Jun 27, 2026
@streamer45 streamer45 self-requested a review June 27, 2026 13:26
@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Open in Devin Review (Staging)
Debug

Playground

Comment thread apps/skit/src/permissions.rs Outdated
Comment on lines +170 to +171
"streamkit::http_input".to_string(),
"streamkit::http_output".to_string(),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: http_input/http_output are implicitly allowed for oneshot regardless of allowlist

Adding streamkit::http_input and streamkit::http_output to the user role's allowed_nodes is harmless but redundant for the oneshot path: apps/skit/src/server/oneshot.rs:529-536 explicitly skips these marker kinds, treating them as implicitly allowed whenever oneshot execution is permitted. The allowlist entries only have an effect at the other is_node_allowed call sites (sessions/websocket/mcp/validation), where these oneshot-only marker nodes would not normally appear. Net: the entries are safe and the documented gateway example is still correct, just over-specified for oneshot use.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks — agreed, info-only and correct. The oneshot path (server/oneshot.rs) skips these marker kinds so they're implicitly allowed there; the allowlist entries only matter at the session/websocket/mcp/validation is_node_allowed call sites. I'm keeping them for consistency with the documented gateway role example (and the new samples/skit.toml role) so the same allowlist works whether or not a node is gated — over-specified but not incorrect.

Tighten the sample user role to deny transport::http::fetcher (was
allowed via the transport::* wildcard) and add a least-privilege
gateway role example.

Signed-off-by: streamkit-devin <devin@streamkit.dev>

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

Open in Devin Review (Staging)
Debug

Playground

Comment thread samples/skit.toml Outdated
Comment thread samples/skit.toml
]

# Only the specific plugin(s) the gateway needs (example: servo for web-capture).
allowed_plugins = ["plugin::native::servo"]

@staging-devin-ai-integration staging-devin-ai-integration Bot Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🚩 Gateway example references plugin::native::servo

Both the docs (docs/src/content/docs/guides/authorization.md:138) and sample config (samples/skit.toml:535) use plugin::native::servo as the gateway's allowed plugin for the web-capture example. Reviewer may want to confirm a servo native plugin kind actually exists/is the right name used by the web-capture example, otherwise the example would be non-functional as written.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed valid — plugin::native::servo is the correct kind. The servo plugin (plugins/native/servo/plugin.yml) declares kind: native, node_kind: servo, and the existing sample pipelines (samples/pipelines/oneshot/web_capture.yml, web_pip_compositor.yml) use kind: plugin::native::servo. So the gateway example is functional as written.

@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.01%. Comparing base (c783f68) to head (4f8ebd0).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #634      +/-   ##
==========================================
- Coverage   85.01%   85.01%   -0.01%     
==========================================
  Files         249      249              
  Lines       74801    74818      +17     
  Branches     2437     2437              
==========================================
+ Hits        63595    63609      +14     
- Misses      11200    11203       +3     
  Partials        6        6              
Flag Coverage Δ
backend 85.05% <100.00%> (-0.01%) ⬇️

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

Components Coverage Δ
core 85.64% <ø> (ø)
engine 83.76% <ø> (ø)
api 91.14% <ø> (ø)
nodes 84.57% <ø> (ø)
server 84.92% <100.00%> (-0.01%) ⬇️
plugin-native 84.79% <ø> (ø)
plugin-wasm 95.41% <ø> (ø)
ui-services 86.27% <ø> (ø)
ui-components 70.18% <ø> (ø)
Files with missing lines Coverage Δ
apps/skit/src/permissions.rs 97.75% <100.00%> (+0.09%) ⬆️

... 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Mirror Permissions::user() in samples/skit.toml: drop blanket core::*
(which exposed core::file_writer) for the safe core subset, add video::*
and plugin usage, keeping transport::http::fetcher denied. Extend the
sample config test to assert the user/gateway node allowlists.

Signed-off-by: streamkit-devin <devin@streamkit.dev>

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 2 new potential issues.

Open in Devin Review (Staging)
Debug

Playground

Comment thread samples/skit.toml
Comment on lines +538 to +543
allowed_nodes = [
"transport::http::mse", # serve live casts to the browser (MSE)
"streamkit::http_input", # oneshot request body
"streamkit::http_output", # oneshot response
"core::*",
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: Gateway role's core::* allows arbitrary-write file_writer despite least-privilege framing

The new gateway role in samples/skit.toml:538-543 and the docs example (docs/src/content/docs/guides/authorization.md:132-137) grant core::*, which includes core::file_writer. The built-in user role (apps/skit/src/permissions.rs:172-181) deliberately enumerates individual core nodes and excludes core::file_writer because of arbitrary-write risk. So the role labelled "least-privilege" is actually broader (for core nodes) than the user role with respect to file_writer. This is a config/security-policy choice rather than a code bug, and security findings are out of scope for this review, but the inconsistency with the stated least-privilege intent may be worth a deliberate decision.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: the gateway example (sample config + docs) now grants only the safe core plumbing nodes (passthrough, file_reader, pacer, sink) instead of core::*, so it no longer exposes core::file_writer — consistent with the least-privilege intent and the built-in user role.

Comment thread samples/skit.toml
Comment on lines 483 to 484
"user/*.yaml",
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: Sample user role allowed_samples diverges slightly from code default

The samples/skit.toml user role's allowed_samples includes demo/*.yml/demo/*.yaml (lines 480-481), while the code default Permissions::user() (apps/skit/src/permissions.rs:149-157) does not. This is a pre-existing divergence not introduced by this PR (the PR only touched allowed_nodes/allowed_plugins for the user role), so it is not a regression, but the code and sample are not a perfect mirror of each other despite the PR's stated goal of mirroring Permissions::user().

(Refers to lines 475-484)

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: removed demo/* from the sample user role's allowed_samples so it now mirrors the built-in Permissions::user() (oneshot/dynamic/user only).

Drop core::* from the gateway example (it exposed core::file_writer,
contradicting the least-privilege intent) for the safe core plumbing
subset, and remove demo/* from the sample user role's allowed_samples
to match the built-in Permissions::user().

Signed-off-by: streamkit-devin <devin@streamkit.dev>
The gateway example could never run its own pipeline: is_node_allowed()
runs before the plugin check, so plugin::native::servo (absent from
allowed_nodes) was rejected, and the role lacked the video/containers nodes
the servo->encode->mux->serve pipeline needs. Grant exactly those kinds.

Also align the sample user role with Permissions::user(): add the
list/read/write/delete_samples flags (without which allowed_samples is
unreachable) and core::param_bridge (a safe in-graph node). Drop the
streamkit::http_input/http_output allowlist entries from both the built-in
and sample roles - they are oneshot-only markers never gated by
is_node_allowed, so listing them was dead config.

Signed-off-by: streamkit-devin <devin@streamkit.dev>

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

Open in Devin Review (Staging)
Debug

Playground

Comment thread samples/skit.toml
Comment on lines +474 to +479
# Sample management (mirrors the built-in Permissions::user()); without these
# flags the allowed_samples list below would be unreachable.
list_samples = true
read_samples = true
write_samples = true
delete_samples = true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: TOML user role now aligned with code, fixing previously-unreachable sample allowlist

Before this PR the samples/skit.toml user role omitted list_samples/read_samples/write_samples/delete_samples, which default to false. Because a TOML-defined role fully replaces the built-in Permissions::user(), the allowed_samples list was effectively unreachable for the sample config's user role. Adding these flags (samples/skit.toml:476-479) corrects that and brings the TOML in line with the code defaults in apps/skit/src/permissions.rs. This is a real fix, not a regression.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct — this was the fix for the second validation finding. Without list/read/write/delete_samples the allowed_samples list was dead config in the sample's user role. Now the sample matches Permissions::user() and the list is reachable.

Comment thread samples/skit.toml
Comment on lines 487 to 514
"user/*.yml",
"user/*.yaml",
]

# Users can use most nodes except potentially dangerous ones
# Users can use most nodes except potentially dangerous ones. Mirrors the
# built-in Permissions::user() default. Allowed: MoQ + the HTTP `mse` sink
# (serves over the caller's own request). Denied by omission:
# transport::http::fetcher (SSRF), core::file_writer / core::object_store_writer
# (arbitrary/external write). The oneshot streamkit::http_input/http_output
# markers are implicitly allowed on the oneshot path and need no entry here.
allowed_nodes = [
"audio::*",
"transport::*",
"core::*",
"video::*",
"containers::*",
"transport::moq::*",
"transport::http::mse",
"core::passthrough",
"core::file_reader",
"core::pacer",
"core::param_bridge",
"core::json_serialize",
"core::text_chunker",
"core::script",
"core::telemetry_tap",
"core::telemetry_out",
"core::sink",
"plugin::*",
]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: User role node allowlist tightened from broad wildcards to explicit list

The sample user role previously allowed transport::* and core::*, which is broader than the code's Permissions::user(). This PR replaces those with an explicit list matching the code (e.g. transport::moq::*, transport::http::mse, and an enumerated set of core:: nodes), removing transport::rtmp, core::file_writer, core::object_store_writer, etc. from the user role. This is an intentional behavior change that could break existing user-created pipelines relying on the removed nodes; worth confirming there are no shipped user//oneshot/ sample pipelines that depend on them (none currently exist under samples/pipelines/user).

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch on the breakage surface. I checked the shipped pipelines: oneshot/tts_to_s3.yml, oneshot/transcode_to_s3.yml, dynamic/moq_s3_archive.yml use core::object_store_writer, and dynamic/moq_to_rtmp_composite.yml uses transport::rtmp::publish. A user can still read those samples but no longer run them.

That's intentional and not a regression relative to the secure default: the built-in Permissions::user() already denies object_store_writer (external write) and rtmp::publish (egress) — the old sample's core::*/transport::* wildcards were the outlier. Those pipelines remain runnable under admin (or a custom role that opts into the egress/write nodes). The point of this PR is to make the sample mirror that secure default.

Comment thread samples/skit.toml
Comment on lines +548 to +559
allowed_nodes = [
"plugin::native::servo", # render the page (web-capture)
"video::pixel_convert", # servo RGBA -> encoder input format
"video::vp9::encoder", # encode to VP9
"containers::webm::muxer", # mux into WebM for MSE / http_output
"transport::http::mse", # serve the live cast to the browser (MSE)
"core::pacer",
"core::sink",
]

# Only the specific plugin(s) the gateway needs (example: servo for web-capture).
allowed_plugins = ["plugin::native::servo"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: Plugin enforcement ordering relied upon by gateway role

The gateway role lists plugin::native::servo in both allowed_nodes and allowed_plugins. This is required because enforcement checks is_node_allowed(kind) before the plugin allowlist (e.g. apps/skit/src/websocket_handlers.rs:55, apps/skit/src/server/sessions.rs:327). Verified the glob matching via glob::Pattern treats * ordinarily across :: (no / separators), so the literal plugin::native::servo entry and plugin::* entries resolve correctly. The new tests cover this ordering dependency.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right — this ordering dependency (is_node_allowed runs before the plugin allowlist) was the root cause of the original gateway-role bug, where plugin::native::servo was in allowed_plugins but absent from allowed_nodes and so rejected first. The new sample_config_test assertion (gateway.is_node_allowed("plugin::native::servo")) plus the docs [!IMPORTANT] note now guard against regressing it.

The documented gateway role serves a video-only WebM cast. Page audio
would also need audio::opus::encoder in allowed_nodes, since the mse node
advertises codecs="vp9,opus". Noted as a future extension.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
staging-devin-ai-integration[bot]

This comment was marked as resolved.

The sample user role's allowed_assets only listed audio, while
Permissions::user() also grants image and font assets. Add the image/font
patterns so the sample matches the built-in default the comments claim to
mirror, and assert the asset policy in sample_config_test.

Signed-off-by: streamkit-devin <devin@streamkit.dev>

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Open in Devin Review (Staging)
Debug

Playground

Comment thread samples/skit.toml Outdated
Comment on lines +531 to +564
# Least-privilege role for trusted intermediaries (e.g. the speech-gateway or
# web-capture examples) that build a small set of fixed pipelines. Avoids
# granting admin just to use HTTP-transport nodes.
create_sessions = true
destroy_sessions = true
list_sessions = true
modify_sessions = true
tune_nodes = true
load_plugins = false
delete_plugins = false
list_nodes = true
access_all_sessions = false # Only its own sessions
upload_assets = false
delete_assets = false

allowed_samples = []

# Exactly the node kinds the web-capture / live-cast pipeline needs, nothing more.
# A plugin kind must be allowed here too: is_node_allowed() runs before the
# allowed_plugins check, so a plugin missing from allowed_nodes is rejected first.
# No fetcher (SSRF) and no file_writer (arbitrary write). The oneshot
# streamkit::http_output marker is implicitly allowed and needs no entry.
allowed_nodes = [
"plugin::native::servo", # render the page (web-capture)
"video::pixel_convert", # servo RGBA -> encoder input format
"video::vp9::encoder", # encode to VP9
"containers::webm::muxer", # mux into WebM for MSE / http_output
"transport::http::mse", # serve the live cast to the browser (MSE)
"core::pacer",
"core::sink",
]

# Only the specific plugin(s) the gateway needs (example: servo for web-capture).
allowed_plugins = ["plugin::native::servo"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: Gateway role comment mentions speech-gateway but only enables web-capture nodes

The comment introducing the new role (samples/skit.toml:531-532) says the gateway role is for "trusted intermediaries (e.g. the speech-gateway or web-capture examples)", but the allowed_nodes/allowed_plugins list only covers the web-capture pipeline (plugin::native::servo, video::pixel_convert, video::vp9::encoder, containers::webm::muxer, transport::http::mse, core::pacer, core::sink). The speech-gateway example would need audio nodes and speech plugins (e.g. whisper/kokoro), so this role as written would not actually support it. This is a documentation/example inaccuracy rather than a functional bug, since the role is just a starter template that operators are expected to customize.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right — the comment overclaimed. Fixed in 4f8ebd0: the comment now states the node/plugin lists are scoped to the web-capture example, and that a different gateway (e.g. speech-gateway) would swap in its own nodes/plugins (audio nodes, whisper/kokoro, etc.). The role stays a web-capture-specific starter template rather than pretending to cover both.

The comment claimed the role serves the speech-gateway too, but the
node/plugin lists only cover web-capture. Clarify that the lists are
web-capture-scoped and other gateways swap in their own nodes/plugins.

Signed-off-by: streamkit-devin <devin@streamkit.dev>

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Open in Devin Review (Staging)
Debug

Playground

Comment thread samples/skit.toml
Comment on lines 515 to +517

# Users cannot load plugins, so this list is empty
allowed_plugins = []
# Users cannot load/delete plugins, but may use plugins an admin has already loaded.
allowed_plugins = ["plugin::*"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

📝 Info: User role permissions widened to include loaded plugins

The built-in Permissions::user() and the samples/skit.toml user role now grant plugin::* in both allowed_nodes and allowed_plugins (previously the sample TOML had allowed_plugins = []). This is a deliberate widening so users can use already-loaded plugins, but it does change the default security posture: any plugin an admin has loaded becomes usable by all user-role callers. Worth confirming this matches the intended access model, especially given native plugins run in-process without a sandbox.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — this aligns the sample with the built-in Permissions::user(), which already grants plugin::* in both lists; the sample's old allowed_plugins = [] was the outlier (and was internally inconsistent, since its allowed_nodes had core::*/transport::* but no plugin grant). The access model is unchanged from the code default: load_plugins = false still means a user cannot load a plugin — only an admin can, and users may then use what the admin has vetted. The in-process/no-sandbox risk you note is real but lives at the admin's load decision (and [plugins].allow_http_management), which is exactly where the existing IMPORTANT comment in Permissions::user() points. Operators who want a stricter posture can narrow allowed_plugins to specific kinds (as the gateway role demonstrates).

@streamer45 streamer45 merged commit 589fc7b into main Jun 28, 2026
30 checks passed
@streamer45 streamer45 deleted the devin/1782566765-user-role-http-nodes branch June 28, 2026 09:40
streamer45 added a commit that referenced this pull request Jun 28, 2026
Leverage two just-merged backend PRs (auth #634, fMP4 MSE #637):

- cast can now serve H.264 in fragmented MP4 via new h264-sw/h264-hw cast
  profiles — http::mse auto-detects fMP4 (#637), so a plain <video> plays the
  live stream in Safari/iOS. The cast muxer is chosen per profile now (WebM for
  VP9/AV1, fMP4 for H.264); VP9/WebM stays the default for crisp screen text on
  Chromium/Firefox.
- Docs: the gateway no longer needs an admin token — the built-in user role now
  allows transport::http::mse (#634), so a user-role token covers the whole
  pipeline. (The demo's token minting is updated separately.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: streamer45 <cstcld91@gmail.com>
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.

2 participants