When I execute dynamically supplied Hyperlambda, I do not start by asking whether the caller seems trustworthy.
I start by asking a much simpler question.
What is this code actually allowed to do?
That is the real question.
Because if I let some partially untrusted source pass me Hyperlambda, whether that source is a user, an AI model, a plugin, or some piece of generated logic, then the security boundary cannot be trust.
It has to be runtime restriction.
That is exactly why I use whitelist.
The whitelist slot gives me a way to create a temporary execution scope where only a very small subset of Hyperlambda slots are available. Inside that scope, the code does not get access to the full runtime vocabulary. It only gets access to what I explicitly allow.
That is the part I care about.
Not whether the caller behaves nicely. Not whether the prompt was polite. Not whether the generated code looks harmless.
What matters is what the runtime lets it invoke.
In this article I will show how I use whitelist, how it works, why its isolation semantics matter, and how to think about it when you want to execute partially untrusted Hyperlambda safely.
What whitelist actually does
At a practical level, whitelist temporarily changes what slots are available inside a given scope.
That means I can declare a block of lambda and say that inside this block, only these exact slots are legal to invoke.
Everything else is unavailable.
This is important because it gives me a clean execution boundary.
Instead of letting the code run against all of Hyperlambda and then hoping it does not touch something dangerous, I can restrict the vocabulary up front.
That changes the problem completely.
I no longer need to reason about every possible thing the caller might try to do with the full language.
I only need to reason about the much smaller vocabulary I deliberately exposed.
That is a much safer place to start.
The two arguments whitelist takes
The whitelist slot takes two arguments.
vocabulary.lambda
The vocabulary node contains the slots that are allowed inside the restricted scope.
The .lambda node contains the Hyperlambda code that should execute under those restrictions.
Conceptually, it looks like this.
whitelist
vocabulary
set-value
return
.lambda
set-value:x:@some-node
.:foo
return
result:success
The important detail is that the .lambda body does not execute with normal unrestricted access.
It executes inside the temporary vocabulary defined by vocabulary.
That means if I do not whitelist a slot, the code cannot invoke it.
That is the whole point.
A minimal example
Let us start with the example from the documentation, because it captures the semantics very well.
.result
whitelist
vocabulary
set-value
return
.lambda
// Inside of this [.lambda] object, we can only invoke [set-value], and no other slots!
set-value:x:@.result
.:foo
// Notice, the next line will throw an exception if you remove its "." character,
// because [add] is not whitelisted in our above [vocabulary] declaration!
.add:x:@.result
.
foo:bar
return
result:success
This example is small, but there is a lot going on.
First, I whitelist only two slots.
set-valuereturn
That means the lambda can use those two slots and nothing else.
Second, the .lambda body tries to set .result.
Third, it includes .add with a leading dot, which means it is treated as a literal node and not an executable slot. If I removed that dot and wrote add instead, it would fail, because add is not in the whitelist.
Finally, the lambda returns a result back to the caller.
That last part matters a lot, because whitelist is not just about blocking access. It is also about defining a controlled way for the restricted code to communicate back.
How vocabulary becomes the security boundary
This is really the part I care about most.
Inside whitelist, the vocabulary node becomes the effective execution boundary.
If a slot is listed there, the lambda can invoke it.
If it is not listed there, the lambda cannot invoke it.
That is a very clean model.
It means I do not have to trust the caller to avoid dangerous operations. I do not have to trust generated code to remain well-behaved. I do not have to rely on a comment or convention saying please do not call powerful slots.
The runtime simply refuses to expose anything outside the declared vocabulary.
That is exactly how I think a security boundary should work.
If I am allowing some external or partially untrusted source to pass me Hyperlambda, then my first job is not to inspect whether the code looks nice.
My first job is to make sure the code cannot bind to capabilities I did not explicitly approve.
That is what whitelist gives me.
Why the lambda is isolated and immutable
This is where whitelist becomes much more interesting than a simple allowed-list of slot names.
The .lambda object inside a whitelist invocation is immutable, and the caller cannot access nodes outside of the .lambda object itself.
That matters a lot.
Because if the restricted code could freely traverse the surrounding tree, then even a narrow vocabulary could still become dangerous.
The code might not be able to call every slot, but it could still potentially inspect or manipulate surrounding state.
whitelist prevents that.
The lambda is isolated from the rest of the program.
That means the restricted code does not get to read outside nodes just because they happen to exist nearby. It does not get to mutate outside nodes just because it can reference them syntactically. And it does not get to treat the surrounding scope as its playground.
That is exactly the behavior I want when code is not fully trusted.
I want the execution boundary to be real.
Not social. Not implied. Real.
Why set-value does not mutate the outer scope
This is one of the most important semantic details to understand.
At first glance, the example seems to suggest that this line should mutate the outer .result node.
set-value:x:@.result
.:foo
But it does not.
And that is intentional.
Inside whitelist, the caller cannot actually reach out and mutate nodes outside the restricted .lambda scope. The invocation creates its own result stack object, and the execution behaves more like a dynamic slot invocation than normal unrestricted code execution.
The documentation describes this as yielding a null node-set result.
That is why the outer .result node does not actually change.
This is one of those places where Hyperlambda semantics are doing security work.
The syntax may look familiar, but the execution model is different because the code is isolated.
That is a good thing.
If I am executing partially untrusted Hyperlambda, I do not want it to be able to casually mutate my outer scope just because it tried to call set-value with what looks like an external reference.
I want isolation to win.
And here, it does.
How return becomes the safe way out
Because the lambda runs in isolation, it still needs some way to communicate back to the caller.
That is where return comes in.
whitelist creates its own result stack object, which means the restricted code can safely return values and nodes to the caller without gaining direct access to outer scope mutation.
That is a very elegant design.
It means the restricted code gets one controlled output channel.
Not unrestricted access. Not shared mutable state. A controlled return path.
That makes the whole model much easier to reason about.
If I allow return, then I know the lambda can send data back.
If I do not allow something else, then it cannot invent that capability on its own.
This is exactly the kind of explicitness I want in a secure runtime.
A more realistic example
Let us look at a more practical example.
Suppose I want to allow some partially untrusted Hyperlambda to produce a simple result, but I only want it to be able to set values and return structured output.
Then I could do something like this.
whitelist
vocabulary
set-value
return
.lambda
.message
set-value:x:@.message
.:Hello world
return
status:ok
message:x:@.message
Here, the lambda can do two things.
- Create local state inside its own restricted scope
- Return data back to the caller
What it cannot do is just as important.
It cannot suddenly start calling file slots. It cannot invoke database slots. It cannot use arbitrary runtime vocabulary. It cannot reach out into the caller's outer execution state and treat it as writable memory.
That is what makes this useful.
The code is not trusted in the broad sense.
But it is constrained.
And in practice, constrained execution is what matters much more.
What I would and would not whitelist
When I use whitelist, I try to think about the exposed vocabulary as an authority surface.
Every slot I include is something the restricted code is now allowed to do.
So I prefer to start very small.
If the lambda only needs to construct a value and return it, then I whitelist only the slots required for that.
I do not widen the vocabulary just because it might be convenient.
That is exactly how overexposure happens.
If I whitelist too much, then I am no longer really creating a narrow execution boundary. I am just pretending to.
So my default approach is simple.
Start with the smallest possible vocabulary. Only add capabilities when there is a concrete reason. Treat every added slot as a deliberate increase in authority.
That is the same mindset I apply to any other security boundary.
whitelist is no different.
What I think matters most about this slot
What I like most about whitelist is not just that it blocks slots.
It is that it expresses a deeper design principle.
Security should come from runtime semantics.
Not from trusting the caller. Not from assuming generated code will behave. Not from hoping comments, conventions, or prompts are enough.
The runtime should be the thing that decides what code is allowed to do.
That is why whitelist fits so naturally into how I think about Hyperlambda security as a whole.
If some code is not fully trusted, then it should not get full authority.
That sounds obvious.
But a surprising number of systems still behave as though generated code, plugin code, or user-supplied logic should be trusted first and constrained later.
I think that is backwards.
I want constraints first. Then execution.
whitelist gives me exactly that.
My takeaway
When I use whitelist, I am not trying to make untrusted code magically trustworthy.
I am doing something much more practical.
I am restricting what that code is allowed to invoke, isolating it from surrounding state, and giving it a controlled way to return data without giving it broad authority over the runtime.
That is why I think whitelist is such an important slot in Hyperlambda.
It takes the security boundary out of intention and puts it into execution semantics.
And that is exactly where I think the boundary belongs.
If I am ever going to let partially untrusted Hyperlambda run, I do not want promises.
I want restrictions.
That is what whitelist gives me.