Skip to content

Project Secrets

Building something that talks to a real API? You will need an API key — and pasting it straight into your code is a bad idea: projects are shareable, forkable, and visible to anyone with the link.

Secrets solve this. A secret is a name + value pair (for example OPENAI_API_KEY = sk-…) that DojoCode stores encrypted and injects into your project's runtime as an environment variable. Your code reads the name; the value never appears in your files, in the editor, or to anyone else.

Write-only by design

Once saved, a secret's value can never be viewed again — not by you, not by other users, not by the AI assistant. You can only replace or delete it. If you forget a value, set a new one.

How secrets work

  • Two scopes. Account secrets (global) are yours everywhere — every project you open can use them. Project secrets are declared on a single project.
  • Project overrides global. If a project secret and an account secret share the same name, the project value wins inside that project.
  • Everyone brings their own values. When another user opens your project, they provide their own values for the secrets your project needs. Your values are never shared — secrets are private per user, per project.
  • Encrypted at rest, injected at runtime. Values are AES-encrypted in the database and only decrypted when your code actually runs — in the browser preview or in the Run sandbox.

Account secrets (Settings)

Manage your global secrets from Account Settings → Secrets — the last tab on your settings page.

Each secret has:

FieldPurpose
NameThe environment variable your code reads, e.g. OPENAI_API_KEY.
Description (optional)What the secret is for. Also shown to the AI assistant so it knows the secret exists — the value never is.
ValueWrite-only. Displayed as •••••••• forever after.

Click New secret, give it a name, an optional description and the value — that's it:

Project secrets (the Secrets tab)

Every project editor — edit page and view page alike — has a Secrets tab next to Details.

As the project author (edit page): secrets you add here become the project's requirements. They override your same-named account secrets, and everyone who opens the project is asked to provide their own values for these names. Deleting one removes it from the project's requirements and deletes every user's stored value for it.

As a visitor (view page): the Secrets tab shows the names the author declared, whether you have set a value for each, and lets you add extra secrets of your own for this project — even if the author declared none. If a required name matches one of your account secrets, it is covered automatically.

If the project requires secrets you have not set, a prompt offers to open the Secrets tab. You can skip it — unset secrets are simply undefined at runtime:

Guests can't open projects that use secrets — signing in is required:

Naming rules and limits

  • Names are UPPER_SNAKE_CASE: start with an uppercase letter or _, then uppercase letters, digits and underscores — OPENAI_API_KEY, DB_URL, _INTERNAL_TOKEN.
  • Reserved prefixes you can't use: DOJOCODE_, VITE_DOJOCODE_, NODE_, NPM_, PATH.
  • Up to 100 account secrets and 100 secrets per project; values up to 8 KB.

Reading secrets in your code

How you read a secret depends on the template's runtime. Quick reference:

TemplatesRead with
React, ReactTS, Vue, VueTS, Svelte, SolidJS, SolidTS, VanillaJS, VanillaTS, React Native, React Native TSimport.meta.env.VITE_NAME
SvelteKit, SvelteKit TS, Remix, Remix TSimport.meta.env.VITE_NAME (client) · process.env.NAME (server code)
Next.js, Next.js TSprocess.env.NAME (Server Components, API routes)
Astro, AstroTSimport.meta.env.NAME
Angularimport { DOJOCODE_ENV } from './dojocode-env'; then DOJOCODE_ENV['NAME']
NodeJS, NodeTS, Fastify, Hono, NestJSprocess.env.NAME
Pythonos.environ["NAME"]
JavaSystem.getenv("NAME")
C#System.Environment.GetEnvironmentVariable("NAME")
Cgetenv("NAME")
C++std::getenv("NAME")
Goos.Getenv("NAME")
Ruststd::env::var("NAME")
RubyENV["NAME"]
PHPgetenv('NAME')
Solidityvm.envString("NAME")
PGlite, SQLiteNot available — SQL playgrounds have no runtime that reads environment variables.

All the examples below assume a secret named API_KEY.

Browser templates (Vite)

Browser code can only see variables prefixed with VITE_, so DojoCode exposes every secret under a VITE_ alias too. This is safe because the preview only ever runs your own values on your own temporary origin — and projects that use secrets can't be published.

jsx
export default function App() {
  return <h1>Key: {import.meta.env.VITE_API_KEY}</h1>;
}
vue
<script setup>
const apiKey = import.meta.env.VITE_API_KEY;
</script>

<template>
  <h1>Key: {{ apiKey }}</h1>
</template>
svelte
<h1>Key: {import.meta.env.VITE_API_KEY}</h1>
jsx
export default function App() {
  return <h1>Key: {import.meta.env.VITE_API_KEY}</h1>;
}
js
document.querySelector('#app').textContent = import.meta.env.VITE_API_KEY;
jsx
import { View, Text } from 'react-native';

export default function App() {
  return (
    <View>
      <Text>Key: {import.meta.env.VITE_API_KEY}</Text>
    </View>
  );
}

Full-stack templates

tsx
// src/app/page.tsx — Server Components and API routes read process.env
export default function Home() {
  return <h1>Key: {process.env.API_KEY}</h1>;
}
astro
---
const apiKey = import.meta.env.API_KEY;
---
<h1>Key: {apiKey}</h1>
svelte
<!-- src/routes/+page.svelte -->
<h1>Key: {import.meta.env.VITE_API_KEY}</h1>
<!-- In +page.server.js / hooks, use process.env.API_KEY -->
tsx
// app/routes/_index.tsx
export default function Index() {
  return <h1>Key: {import.meta.env.VITE_API_KEY}</h1>;
}
// In loaders/actions (server), use process.env.API_KEY

Angular

Angular's build has no environment-variable mechanism, so DojoCode mounts a virtual module called dojocode-env.ts at the project root whenever you have secrets. It shows up read-only in the file tree so you can see which names exist — the values inside are only filled in inside the running preview, never in the editor.

ts
// app.component.ts
import { Component } from '@angular/core';
import { DOJOCODE_ENV } from './dojocode-env';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  apiKey = DOJOCODE_ENV['API_KEY'];
}

WARNING

Never create or edit dojocode-env.ts yourself — the platform manages it, and it is never saved into your project.

Backend JavaScript templates

Works the same in NodeJS, NodeTS, Fastify, Hono and NestJS:

js
console.log(process.env.API_KEY);

Python

python
import os

print(os.environ["API_KEY"])          # raises KeyError if unset
print(os.environ.get("API_KEY"))      # None if unset

Compiled & other languages

These templates run in the Output panel (Run button); the secrets are injected into the run environment.

java
public class HelloWorld {
    public static String hello() {
        return System.getenv("API_KEY");
    }
}
csharp
namespace Challenge
{
    public class HelloWorld
    {
        public static string Hello()
        {
            return System.Environment.GetEnvironmentVariable("API_KEY");
        }
    }
}
c
#include <stdlib.h>
#include <stdio.h>

void print() {
    printf("%s\n", getenv("API_KEY"));
}
cpp
#include <cstdlib>
#include <iostream>

int main() {
    std::cout << std::getenv("API_KEY") << std::endl;
}
go
package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Println(os.Getenv("API_KEY"))
}
rust
fn main() {
    println!("{}", std::env::var("API_KEY").unwrap_or_default());
}
ruby
puts ENV["API_KEY"]
php
<?php
echo getenv('API_KEY');
solidity
// In a Foundry script (Main.s.sol)
import "forge-std/Script.sol";
import "forge-std/console.sol";

contract Main is Script {
    function run() external {
        console.log(vm.envString("API_KEY"));
    }
}

SQL playgrounds

The PGlite and SQLite templates execute .sql files only — there is no user code that could read an environment variable, so secrets are not available there and the Secrets tab is hidden.

Secrets and publishing

A published site (*.dojocode.net) is a static, public build — any secret value used at build time would end up readable in the bundle. To keep your keys safe, publishing is blocked while a project uses secrets:

  • the project declares project secrets, or
  • its code references any of your account secret names.

The Publish dialog explains exactly which names are blocking and stays locked until you remove the references (or rename them). Owner-set deployment variables are planned for a future release.

Secrets and Alex (the AI assistant)

Alex sees the names and descriptions of the secrets available to the project — never the values. That way it can write code that reads process.env.API_KEY for you, guard for missing values, and point you to the Secrets panel when a feature needs a key you haven't added yet. It will refuse to hard-code secret values or write .env files, since those would be saved into the project for everyone to see.

Good to know

  • Skipping is allowed. A visitor who skips the secrets prompt can still run the project — unset secrets are just undefined/empty at runtime, so guard for missing values.
  • Forks copy no secrets. Fork a project and it starts with a clean slate; declare what it needs again.
  • Updating keeps the old value if you leave it blank. Editing a secret's description without typing a new value leaves the stored value untouched.
  • Preview restarts automatically when your secrets change, so the new values are picked up without a manual reload.