Skip to content

Val Permissions

There are three privacy settings for a Val:

PermissionsVisible on the websiteWeb & express endpoints
PublicYes
UnlistedOnly if you have the link
PrivateNoRequires API token

You get to choose whether you share vals. They can be fully private, so only you can view, edit, and run them. Private vals are super useful for automating parts of your life or business. Or you can share vals publicly in the Val Town community, and let anyone learn from your creativity and expand on your work.

Exposing your vals to the internet

Public vals are great because they can be called from anywhere, anytime, instantly.

Since anyone can call your public endpoints, if they interact with some data that should only be changed by yourself, you will need to make sure that those endpoints check for some kind of secret that only you know.

Here’s an example of a val exposed using the HTTP Val, secured with an environment variable that only I know.

user/secretEndpointRun in Val Town ↗
export const secretEndpoint = (req: Request) => {
const secretHeader = req.headers.get("Authorization");
if (secretHeader !== Deno.env.get("supersecrettoken")) {
return new Response("Unauthorized", { status: 401 });
}
return new Response("My deepest darkest secret");
};

If I called it without supplying the environment variable, I’d be denied access:

Without authenticationRun in Val Town ↗
import { fetch } from "https://esm.town/v/std/fetch";
const response = await fetch("https://user-secretEndpoint.web.val.run");
console.log(response);
Logs
Response {
body: ReadableStream { locked: false },
bodyUsed: false,
headers: Headers { /* omitted */ },
ok: false,
redirected: false,
status: 401,
statusText: "Unauthorized",
url: "http://localhost:3001/v1/fetch?url=https%3A%2F%2Fuser-secretEndpoint.web.val.run"
}

By supplying the environment variable in a header, I’m allowed access:

With authenticationRun in Val Town ↗
import { fetch } from "https://esm.town/v/std/fetch";
const response = await fetch("https://user-secretEndpoint.web.val.run", {
headers: { Authorization: "birdsarentreal" },
});
console.log(response);
Logs
Response {
body: ReadableStream { locked: false },
bodyUsed: false,
headers: Headers { /* omitted */ },
ok: true,
redirected: false,
status: 200,
statusText: "OK",
url: "http://localhost:3001/v1/fetch?url=https%3A%2F%2Fuser-secretEndpoint.web.val.run"
}

The rest of this article will focus on various common combinations of public and private vals that you’re likely to come across and how those interact with the permissions system.

Public code referencing private data

It is safe for a a public val to reference one of your private vals or one of your environment variables. Private vals are like environment variables in this way — others can see that they’re being used, but not their values.

For example, I created a private val, example3. You won’t be able to see or reference example3 but I can use it in example4 which is public.

@user/example4
import { example3 } from "https://esm.town/v/user/example3";
console.log("Hi,", example3);
Logs
Hi, User

You can infer that the value of example3 is "User" because of how it’s used here. This is why you have to be careful about publishing vals that reference private data. Typically you will reference private data in a way that makes it impossible for others to infer what it is, like you would with an environment variable credentials. Below I am passing my environment variables to an Upstash Redis store. You can see that I’m using these environment variables and the output of this computation, but you can’t get those values, nor can you rerun this script with my environment variables.

ExampleRun in Val Town ↗
import { Redis } from "npm:@upstash/redis";
const redis = new Redis({
url: Deno.env.get("upstashURL"),
token: Deno.env.get("upstashToken"),
});
await redis.set("json-ex-1", { a: { b: "nested json" } });
const get = await redis.get("json-ex-1");
console.log(get);
Output
nested json

Using another’s vals as a library

You can import any Public or Unlisted vals and use them in your own code. In this way it is safe to pass other’s code your private data and environment variables.

ExampleRun in Val Town ↗
import { gpt3 } from "https://esm.town/v/patrickjm/gpt3?v=4";
export let librarySecret = gpt3({
prompt: "what is the meaning of life?",
openAiKey: Deno.env.get("openai"),
});

Hiding Code

You can hide the code of a public API by wrapping a private Val in a public val. Below I show hiddenAPI which you can see and call via API, but you notice that it calls @stevekrouse.hiddenAPIInternal which is private and can’t be inspected.

Hiding code exampleRun in Val Town ↗
import { hiddenAPIInternal } from "https://esm.town/v/stevekrouse/hiddenAPIInternal";
export let hiddenAPI = (...args) => hiddenAPIInternal(...args);

Custom Authentication

You can roll arbitrary authentication schemes in user space. For example, I find it useful to simply supply a custom auth header from my Clerk webhooks that I check like a password against a value I store in my environment variables:

Clerk exampleRun in Val Town ↗
import { discordWebhook } from "https://esm.town/v/stevekrouse/discordWebhook";
// # New Val Town User (on Clerk) -> Val Town Discord notification
// Translates one kind of webhook (Clerk) into another (Discord)
export async function handleDiscordNewUser(req: express.Request, res) {
// check custom auth secret sent from clerk
if (req.get("auth") !== process.env.clerkNonSensitive)
return res.end("Unauthorized");
await discordWebhook({
url: Deno.env.get("discordUserEvents"),
content:
req.body.data.email_addresses[0].email_address +
" " +
req.body.data.profile_image_url,
});
res.end("Success");
}

I call this value clerkNonSensitive because this value doesn’t protect any data. It merely makes it impossible for anyone to trigger this public API endpoint without the password. The worst that could happen if this password leaks is that our team temporarily gets spam discord messages. Then I could just change the password to a new value. For more sensitive use-cases, you’ll want to sign & possibly encrypt the conveyed data using standard authentication methods.