chore(blog): Blog on rustyscript implementation in superposition#1064
chore(blog): Blog on rustyscript implementation in superposition#1064Datron wants to merge 2 commits into
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughA new blog post ( ChangesRustyScript Blog Post
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| tags: [technical deepdive, rust, js, functions] | ||
| --- | ||
|
|
||
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application |
There was a problem hiding this comment.
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application | |
| TL;DR - We use RustyScript to execute user submitted JS code in a sandboxed fashion inside our rust application. |
There was a problem hiding this comment.
Will replace this with We use RustyScript to sandbox and execute user submitted JS code inside our rust application
|
|
||
| When a function is being edited/created, we also ensured that the function the user provided is valid. | ||
|
|
||
| This was a simple solution we tried initially and it worked well - until users wanted more capabilities. One capability was **Value Compute Functions** - functions that returned a vector of values. Users wanted to select a value from a dynamic list of potential values that changed often which was fetched from internal systems at Juspay. Another downside to our nodeJS isolate solution was a lot of wrapper code to ensure that we properly isolated user code. We didn't validate if the code that was being stored was valid syntatically, and if node could not execute it we didn't return that error to the user. |
There was a problem hiding this comment.
just internal systems is sufficient - need not refer to company name unless absolutely needed.
There was a problem hiding this comment.
Any reason to not mention Juspay? Would help contextualize internal systems and helps readers understand that this is used somewhere
|
|
||
| # Running Javascript in Rust with Rustyscript | ||
|
|
||
| Rustyscript is powerful and extremely customizable for running user code - it has a permissions model, functions that you can expose from rust to use in JS land and some very nice built in functionality like console, crypto, http, etc. |
There was a problem hiding this comment.
| Rustyscript is powerful and extremely customizable for running user code - it has a permissions model, functions that you can expose from rust to use in JS land and some very nice built in functionality like console, crypto, http, etc. | |
| Rustyscript is powerful and extremely customizable for running user submitted JS code - it has a permissions model, functions that you can expose from rust to use in JS land and some very nice built in functionality like console, crypto, http, etc. |
There was a problem hiding this comment.
The title mentions javascript so I don't want to repeat it too many times and the word submitted is implied
|
|
||
| - Create a basic runtime | ||
| - Load a javascript module, | ||
| - Call a function and the resulting value |
There was a problem hiding this comment.
This point seems incomplete - did you mean and
consume the resulting value ?
| @@ -0,0 +1,236 @@ | |||
| --- | |||
| slug: js-sandbox-in-rust | |||
| title: Rust JS Sandbox - Running Untrusted JavaScript Inside Superposition | |||
There was a problem hiding this comment.
Can we change untrusted to user defined ?
There was a problem hiding this comment.
The meaning is the same - we want to isolate and execute the code.
| description: The design and implementation of validation and value compute functions - running user defined JS functions inside rust that is seamless and isolated | ||
| tags: [technical deepdive, rust, js, functions] | ||
| --- | ||
|
|
There was a problem hiding this comment.
Can we add a section for what all we sandboxed ?
There was a problem hiding this comment.
We sandboxed functions only is my understanding, can you give an example of what this section would be
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application | ||
|
|
||
| [Superposition](https://github.com/juspay/superposition) is a context-aware configuration management system that ensures correct, safe and reliable config changes. One of the ways we've achieved this goal is with jsonschema types for all configuration values with a default value. We call this concept [Default Configs](/docs/basic-concepts/context-aware-config/default-config). We soon realized an issue - though the type is right; that does not guarantee the correctness of the actual value. | ||
|
|
There was a problem hiding this comment.
Can we add example of URL to make the argument stronger ?
There was a problem hiding this comment.
Its in the example below
| We came up with functions - a way for any end user to write validation functions and link it to a Default Config or [Dimension](/docs/basic-concepts/context-aware-config/dimensions) to ensure the correctness of that value (should they want to do that). These functions are written in our frontend monaco editor and saved to a database with some other information. The function definition looks like this: | ||
|
|
||
| ``` | ||
| async function execute(payload) -> boolean |
There was a problem hiding this comment.
this is wrong js syntax
async function execute(payload) {
return true; // boolean
}
There was a problem hiding this comment.
This is a function definition, not JS code. Its an example
There was a problem hiding this comment.
But example can be valid JS code right - just don't want to introduce custom code representations which an average reader might not be familiar with or can get confused.
We don't control readers - but we can afford to be clear from get go is what the broader spirit of the comment is.
There was a problem hiding this comment.
Just want to define the function inputs and outputs, the code representation would need a bit more cognitive effort to read this
|
|
||
| # First implementation - NodeJS isolates | ||
|
|
||
| Since we just needed a boolean return value, we wrapped the user code with Isolates and ran it as a nodeJS script. If the function returned false, the code forced the script to terminate with an custom exit code. When this was run through Rust's `std::process::Command`, we checked the exit code and deciphered the return value of the function. Users also asked for logs, so console.log would output to stdout, which we also read and returned to the user. Since this was NodeJS, any npm package like axios for HTTP requests we wanted to make available to the user could just be installed and used along with the script we we're running. |
There was a problem hiding this comment.
Isolates makes it a little confusing.
There was a problem hiding this comment.
That is the technical term
|
|
||
| # First implementation - NodeJS isolates | ||
|
|
||
| Since we just needed a boolean return value, we wrapped the user code with Isolates and ran it as a nodeJS script. If the function returned false, the code forced the script to terminate with an custom exit code. When this was run through Rust's `std::process::Command`, we checked the exit code and deciphered the return value of the function. Users also asked for logs, so console.log would output to stdout, which we also read and returned to the user. Since this was NodeJS, any npm package like axios for HTTP requests we wanted to make available to the user could just be installed and used along with the script we we're running. |
| // value_validate is an object that contains all arguments a validation function would need | ||
| const { key, value, type, environment } = value_validate; | ||
| let response = await fetch(value); | ||
| return response.status == '200'; |
There was a problem hiding this comment.
return response.status == 200;
| export function typeCheck() { | ||
| if (typeof execute === "undefined") { | ||
| throw new Error("execute function is not defined"); | ||
| } | ||
| } |
There was a problem hiding this comment.
export function typeCheck() {
if (typeof execute !== "function") {
throw new Error("execute must be a function");
}
}
There was a problem hiding this comment.
This is what is in superposition
There was a problem hiding this comment.
We should then fix it in superposition then ideally.
|
|
||
| Rustyscript can do a lot more than what we've shared here - I recommend taking a look at the [documentation](https://rscarson.github.io/rustyscript-book/index.html) if you've never used it before. For Superposition, its ability to run isolated javascript seamlessly from within rust has enabled our team to provide more useful features to customers. | ||
|
|
||
| If you like the idea of Superposition, do take a look at our [docs](https://juspay.io/superposition/docs) to learn more. Have questions? Join our [slack](superpositionjp.slack.com) to interact with the team. No newline at end of file |
There was a problem hiding this comment.
Slack link is broken. Can you fix it ?
There was a problem hiding this comment.
Broken how? This is what we have in readme
knutties
left a comment
There was a problem hiding this comment.
Grammar and flow pass on the blog post. Inline comments include suggestion blocks where a concrete replacement is appropriate. A few higher-level notes that don't map cleanly to single lines:
1. Naming consistency (highest-impact single fix). Across the post you have RustyScript / Rustyscript / rustyscript, rust / Rust, nodeJS / NodeJS / Node.js, and javascript / JavaScript / JS. Pick one canonical form for each and do a global pass — the inconsistency is the most distracting thing about the current draft.
2. "First implementation" section buries the most interesting drawback. The strongest motivation for moving off Node isolates — limited capabilities (compute functions returning lists), no syntax pre-validation, heavy wrapper code, process-spawn overhead — is condensed into one dense paragraph (L47). Consider splitting into "What worked" (1–2 sentences) and "Why we outgrew it" (a short bulleted list).
3. "Other options" is a single sentence. Naming why mlua was dropped and what you concretely compared between rquickjs and rustyscript would make this section much more useful to readers evaluating their own embedding choices.
4. Code-heavy middle reads as redundant. The Rust snippets at L100–137 and L184–230 are nearly identical in structure (create runtime → load module → call function → handle error). Consider showing the first one fully, then for the second highlighting only what's new (the log capture, the wrapper) instead of re-printing the whole pattern.
5. Missing transitions. "Other options" jumps straight into "Running JavaScript in Rust with Rustyscript" — a one-line lead-in like "Here's how we wired it up" softens the section break.
Happy to follow up with a second pass once these are addressed.
| @@ -0,0 +1,236 @@ | |||
| --- | |||
| slug: js-sandbox-in-rust | |||
| title: Rust JS Sandbox - Running Untrusted JavaScript Inside Superposition | |||
There was a problem hiding this comment.
Double space after title:.
| title: Rust JS Sandbox - Running Untrusted JavaScript Inside Superposition | |
| title: Rust JS Sandbox - Running Untrusted JavaScript Inside Superposition |
| --- | ||
| slug: js-sandbox-in-rust | ||
| title: Rust JS Sandbox - Running Untrusted JavaScript Inside Superposition | ||
| description: The design and implementation of validation and value compute functions - running user defined JS functions inside rust that is seamless and isolated |
There was a problem hiding this comment.
user defined should be hyphenated, rust should be capitalised, and the that is seamless and isolated clause is grammatically ambiguous (what does that refer to?).
| description: The design and implementation of validation and value compute functions - running user defined JS functions inside rust that is seamless and isolated | |
| description: Designing and implementing validation and value-compute functions: how we run user-defined JS inside Rust, seamlessly and in isolation. |
| tags: [technical deepdive, rust, js, functions] | ||
| --- | ||
|
|
||
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application |
There was a problem hiding this comment.
user-submitted, Rust, and the crate is consistently Rustyscript/rustyscript (not RustyScript) in its own docs — pick one form and apply globally.
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application | |
| TL;DR - We used Rustyscript to sandbox user-submitted JS code inside our Rust application |
|
|
||
| TL;DR - We used RustyScript to sandbox user submitted JS code inside our rust application | ||
|
|
||
| [Superposition](https://github.com/juspay/superposition) is a context-aware configuration management system that ensures correct, safe and reliable config changes. One of the ways we've achieved this goal is with jsonschema types for all configuration values with a default value. We call this concept [Default Configs](/docs/basic-concepts/context-aware-config/default-config). We soon realized an issue - though the type is right; that does not guarantee the correctness of the actual value. |
There was a problem hiding this comment.
A few issues:
- Missing Oxford comma (
correct, safe, and reliable) — used inconsistently elsewhere in the post. One of the ways we've achieved this goal is with … with …double-withreads awkwardly.- The semicolon in
though the type is right; that does not guarantee …is incorrect — should be a comma or em-dash. - Trailing whitespace.
| [Superposition](https://github.com/juspay/superposition) is a context-aware configuration management system that ensures correct, safe and reliable config changes. One of the ways we've achieved this goal is with jsonschema types for all configuration values with a default value. We call this concept [Default Configs](/docs/basic-concepts/context-aware-config/default-config). We soon realized an issue - though the type is right; that does not guarantee the correctness of the actual value. | |
| [Superposition](https://github.com/juspay/superposition) is a context-aware configuration management system that ensures correct, safe, and reliable config changes. One of the ways we achieve this is by requiring a JSON Schema and a default for every configuration value. We call this concept [Default Configs](/docs/basic-concepts/context-aware-config/default-config). We soon realized an issue — though the type is right, that does not guarantee the correctness of the actual value. |
| } | ||
| ``` | ||
|
|
||
| The most strict JSON schema for the value of image_url would be: |
There was a problem hiding this comment.
most strict → strictest.
| The most strict JSON schema for the value of image_url would be: | |
| The strictest JSON schema for the value of image_url would be: |
| } | ||
| } | ||
| ``` | ||
| We validate this with `rustyscript::validate`, which takes a string and tells you if its valid JS syntax: |
There was a problem hiding this comment.
its → it's.
| We validate this with `rustyscript::validate`, which takes a string and tells you if its valid JS syntax: | |
| We validate this with `rustyscript::validate`, which takes a string and tells you if it's valid JS syntax: |
| - Create a basic runtime | ||
| - Load a javascript module, | ||
| - Call a function and the resulting value |
There was a problem hiding this comment.
List items aren't parallel: L96 ends with a comma, L97 is missing a verb (Call a function and the resulting value). Also javascript → JavaScript.
| - Create a basic runtime | |
| - Load a javascript module, | |
| - Call a function and the resulting value | |
| - Create a basic runtime | |
| - Load a JavaScript module | |
| - Call a function and get the resulting value |
| Ok(()) | ||
| ``` | ||
|
|
||
| Now when this function needs to be run to validate any input or change, it is wrapped in the below JS code. Our wrapper code for user functions in rustyscript looks like this, we override console so that it can output logs to an array: |
There was a problem hiding this comment.
Comma splice (looks like this, we override …) and the sentence is doing two things at once. Suggest splitting and leading with what the wrapper actually does so the reader knows what to look for in the snippet.
| Now when this function needs to be run to validate any input or change, it is wrapped in the below JS code. Our wrapper code for user functions in rustyscript looks like this, we override console so that it can output logs to an array: | |
| Now when this function needs to be run to validate any input or change, it is wrapped in the JS code below. Note the console override — it routes every log call into an in-memory buffer that we expose via `getLogBuffer`: |
|
|
||
| # Conclusion | ||
|
|
||
| Rustyscript can do a lot more than what we've shared here - I recommend taking a look at the [documentation](https://rscarson.github.io/rustyscript-book/index.html) if you've never used it before. For Superposition, its ability to run isolated javascript seamlessly from within rust has enabled our team to provide more useful features to customers. |
There was a problem hiding this comment.
The conclusion is thin and doesn't tie back to the opening pain point (correctness beyond types). Quantifying/qualifying the win lands harder.
| Rustyscript can do a lot more than what we've shared here - I recommend taking a look at the [documentation](https://rscarson.github.io/rustyscript-book/index.html) if you've never used it before. For Superposition, its ability to run isolated javascript seamlessly from within rust has enabled our team to provide more useful features to customers. | |
| Rustyscript can do a lot more than what we've shared here - I recommend taking a look at the [documentation](https://rscarson.github.io/rustyscript-book/index.html) if you've never used it before. For Superposition, Rustyscript's ability to run isolated JavaScript seamlessly from within Rust has unlocked richer correctness guarantees — we now support both validation and value-compute functions with full syntax checking and captured logs, without spawning a subprocess. |
|
|
||
| Rustyscript can do a lot more than what we've shared here - I recommend taking a look at the [documentation](https://rscarson.github.io/rustyscript-book/index.html) if you've never used it before. For Superposition, its ability to run isolated javascript seamlessly from within rust has enabled our team to provide more useful features to customers. | ||
|
|
||
| If you like the idea of Superposition, do take a look at our [docs](https://juspay.io/superposition/docs) to learn more. Have questions? Join our [slack](superpositionjp.slack.com) to interact with the team. No newline at end of file |
There was a problem hiding this comment.
[slack](superpositionjp.slack.com) — link is missing the https:// scheme so it renders as a relative path; also capitalise Slack.
| If you like the idea of Superposition, do take a look at our [docs](https://juspay.io/superposition/docs) to learn more. Have questions? Join our [slack](superpositionjp.slack.com) to interact with the team. | |
| If you like the idea of Superposition, do take a look at our [docs](https://juspay.io/superposition/docs) to learn more. Have questions? Join our [Slack](https://superpositionjp.slack.com) to interact with the team. |
@Datron - in case you are wondering what Oxford Comma is :) TIL as well.
|
Signed-off-by: datron <[email protected]>
7816d68 to
96d284d
Compare
Signed-off-by: datron <[email protected]>
96d284d to
e38f4a3
Compare

Add a blog post about rustyscript
Summary by CodeRabbit
Documentation