Skip to content

Commit bec342f

Browse files
committed
chore(rivetkit): impl inspector token
1 parent 1eb664c commit bec342f

7 files changed

Lines changed: 55 additions & 6 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rivetkit-rust/packages/rivetkit-core/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ sqlite = ["dep:rivetkit-sqlite"]
1212

1313
[dependencies]
1414
anyhow.workspace = true
15+
base64.workspace = true
1516
ciborium.workspace = true
1617
futures.workspace = true
1718
http.workspace = true
1819
nix.workspace = true
1920
parking_lot.workspace = true
2021
prometheus.workspace = true
22+
rand.workspace = true
2123
reqwest.workspace = true
2224
rivet-pools.workspace = true
2325
rivet-util.workspace = true

rivetkit-rust/packages/rivetkit-core/src/actor/task.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,9 @@ impl ActorTask {
11191119
.persist_state(SaveStateOpts { immediate: true })
11201120
.await
11211121
.context("persist actor initialization")?;
1122+
crate::inspector::init_inspector_token(&self.ctx)
1123+
.await
1124+
.context("initialize inspector token")?;
11221125
self.ctx
11231126
.restore_hibernatable_connections_with_preload(self.preloaded_kv.as_ref())
11241127
.await

rivetkit-rust/packages/rivetkit-core/src/inspector/auth.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use anyhow::Result;
1+
use anyhow::{Context, Result};
2+
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
3+
use rand::RngCore;
24
use rivet_error::RivetError as RivetErrorDerive;
35
use serde::{Deserialize, Serialize};
46

57
use crate::ActorContext;
68

79
const INSPECTOR_TOKEN_KEY: [u8; 1] = [3];
810
const INSPECTOR_TOKEN_ENV: &str = "RIVET_INSPECTOR_TOKEN";
11+
const INSPECTOR_TOKEN_BYTES: usize = 32;
912

1013
#[derive(Clone, Copy, Debug, Default)]
1114
pub struct InspectorAuth;
@@ -47,6 +50,44 @@ impl InspectorAuth {
4750
}
4851
}
4952

53+
/// Ensures the actor has an inspector token persisted in KV at `[3]` so the
54+
/// engine-facing KV API can serve the token to the dashboard inspector.
55+
/// Skips the write when the token already exists. No-ops when the
56+
/// `RIVET_INSPECTOR_TOKEN` env override is set, since that takes precedence
57+
/// over any KV-stored token and we do not want to pin a per-actor token that
58+
/// will never be consulted.
59+
pub async fn init_inspector_token(ctx: &ActorContext) -> Result<()> {
60+
if std::env::var(INSPECTOR_TOKEN_ENV)
61+
.ok()
62+
.is_some_and(|token| !token.is_empty())
63+
{
64+
return Ok(());
65+
}
66+
67+
let existing = ctx
68+
.kv()
69+
.get(&INSPECTOR_TOKEN_KEY)
70+
.await
71+
.context("load inspector token")?;
72+
if existing.is_some() {
73+
return Ok(());
74+
}
75+
76+
let token = generate_inspector_token();
77+
ctx.kv()
78+
.put(&INSPECTOR_TOKEN_KEY, token.as_bytes())
79+
.await
80+
.context("persist inspector token")?;
81+
tracing::debug!(actor_id = %ctx.actor_id(), "generated new inspector token");
82+
Ok(())
83+
}
84+
85+
fn generate_inspector_token() -> String {
86+
let mut bytes = [0u8; INSPECTOR_TOKEN_BYTES];
87+
rand::thread_rng().fill_bytes(&mut bytes);
88+
URL_SAFE_NO_PAD.encode(bytes)
89+
}
90+
5091
fn verify_token_bytes(candidate: &[u8], expected: &[u8]) -> Result<()> {
5192
if timing_safe_equal(candidate, expected) {
5293
Ok(())

rivetkit-rust/packages/rivetkit-core/src/inspector/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use parking_lot::RwLock;
77
pub mod auth;
88
pub(crate) mod protocol;
99

10-
pub use auth::InspectorAuth;
10+
pub use auth::{InspectorAuth, init_inspector_token};
1111

1212
type InspectorListener = Arc<dyn Fn(InspectorSignal) + Send + Sync>;
1313

rivetkit-rust/packages/rivetkit-core/src/registry/inspector.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ impl RegistryDispatcher {
156156
)
157157
})
158158
}
159+
// TODO: Impelment when ready
159160
(http::Method::GET, "/inspector/traces") => json_http_response(
160161
StatusCode::OK,
161162
&json!({

rivetkit-rust/packages/rivetkit-core/tests/modules/inspector.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ mod moved_tests {
251251
async fn inspector_auth_uses_env_token_before_kv_fallback() {
252252
let _env_guard = INSPECTOR_ENV_LOCK.lock().expect("env lock poisoned");
253253
unsafe {
254-
std::env::set_var("RIVET_INSPECTOR_TOKEN", "env-token");
254+
std::env::set_var("_RIVET_TEST_INSPECTOR_TOKEN", "env-token");
255255
}
256256

257257
let kv = crate::kv::tests::new_in_memory();
@@ -280,15 +280,15 @@ mod moved_tests {
280280
assert_eq!(error.code(), "unauthorized");
281281

282282
unsafe {
283-
std::env::remove_var("RIVET_INSPECTOR_TOKEN");
283+
std::env::remove_var("_RIVET_TEST_INSPECTOR_TOKEN");
284284
}
285285
}
286286

287287
#[tokio::test]
288288
async fn inspector_auth_falls_back_to_actor_kv_token() {
289289
let _env_guard = INSPECTOR_ENV_LOCK.lock().expect("env lock poisoned");
290290
unsafe {
291-
std::env::remove_var("RIVET_INSPECTOR_TOKEN");
291+
std::env::remove_var("_RIVET_TEST_INSPECTOR_TOKEN");
292292
}
293293

294294
let kv = crate::kv::tests::new_in_memory();
@@ -321,7 +321,7 @@ mod moved_tests {
321321
async fn inspector_auth_rejects_missing_token() {
322322
let _env_guard = INSPECTOR_ENV_LOCK.lock().expect("env lock poisoned");
323323
unsafe {
324-
std::env::remove_var("RIVET_INSPECTOR_TOKEN");
324+
std::env::remove_var("_RIVET_TEST_INSPECTOR_TOKEN");
325325
}
326326

327327
let ctx = new_with_kv(

0 commit comments

Comments
 (0)