An agent with open network access can install anything, send your code anywhere, and pass a test by calling a live service. Kestrel turns outbound traffic off. You open it one host at a time.
DNS lookup
A laptop asks for a name; its resolver asks the root of the name tree, then the COM zone, then the domain's own server, each pointing it to the next, and the server's address record lights and its address comes back to the laptop.
w - 1.0 || lr < 0.0 || lr > h - 1.0) return 0.0;
float hs = hash(vec2(s, lr));
float v = 0.18;
if (lc < 1.5) v = 0.62 + 0.08 * sin(ph + hs * 30.0);
else if (lc > 2.5 && lc < 3.0 + floor((w - 3.0) * (0.45 + 0.55 * hs))) v = 0.45;
return (v + 0.55 * lit * step(0.3, v)) * k;
}
float field(vec2 p, float t) {
if (p.x < 24.0 || p.x > 696.0 || p.y < 24.0 || p.y > 436.0) return 0.0;
float ph = TAU * t / T;
float col = floor((p.x - 4.0) / 7.0 + 0.5);
float cell = floor(p.y / 9.4);
float v = 0.0;
float reset = 1.0 - sm(0.3, 0.6, t);
float ans = exp(-pow(t - 1.18, 2.0) / 0.01); // the root answering
float ansC = exp(-pow(t - 2.10, 2.0) / 0.01);
float ansE = exp(-pow(t - 3.02, 2.0) / 0.01);
// the tree: the root's branches and COM's branches, faint; a branch lights once it is the referral given
float toC = max(reset, sm(1.22, 1.32, t)); float toE = max(reset, sm(2.14, 2.24, t));
v = max(v, lk(p, vec2(410.0, 113.700), vec2(354.0, 182.0), 0.3 + 0.32 * toC));
v = max(v, lk(p, vec2(422.0, 111.700), vec2(473.0, 191.400), 0.28));
v = max(v, lk(p, vec2(454.0, 90.700), vec2(599.0, 182.0), 0.28));
v = max(v, lk(p, vec2(354.0, 239.0), vec2(361.0, 296.600), 0.3 + 0.32 * toE));
v = max(v, lk(p, vec2(368.0, 239.0), vec2(494.0, 332.400), 0.28));
v = max(v, lk(p, vec2(378.0, 239.0), vec2(613.0, 323.0), 0.28));
// the root: thirteen server points round a core
for (int i = 0; i < 13; i++) {
float a = TAU * float(i) / 13.0 - 1.5708;
v += blob(p, vec2(410.0, 78.700) + vec2(40.0 * cos(a), 29.0 * sin(a)), 6.5, 0.95 + 0.12 * sin(ph + float(i) * 2.1) + 0.3 * ans);
}
v += blob(p, vec2(410.0, 78.700), 13.0, 0.62 + 0.55 * ans);
{
vec2 d = (p - vec2(410.0, 78.700)) / vec2(30.0, 21.0);
float hs = hash(floor(p / vec2(7.0, 9.4)) + 3.0);
if (length(d) < 1.0) v = max(v, step(0.6, hs) * (0.24 + 0.06 * sin(ph + hs * 40.0)));
}
// the top-level zones and the domains' servers
v = max(v, rows(col, cell, 46.0, 20.0, 9.0, 5.0, 1.0, 1.0, ansC, ph));
v = max(v, rows(col, cell, 64.0, 21.0, 7.0, 4.0, 2.0, 0.7, 0.0, ph));
v = max(v, rows(col, cell, 82.0, 20.0, 7.0, 4.0, 3.0, 0.7, 0.0, ph));
v = max(v, rows(col, cell, 67.0, 36.0, 7.0, 4.0, 4.0, 0.7, 0.0, ph));
v = max(v, rows(col, cell, 84.0, 35.0, 7.0, 4.0, 5.0, 0.7, 0.0, ph));
v = max(v, rows(col, cell, 48.0, 35.0, 11.0, 7.0, 6.0, 1.0, ansE, ph));
// EXAMPLE.COM's A record: its row lights once it is found
if (abs(cell - 38.0) < 0.5 && col >= 48.0 && col <= 58.0) v = max(v, 1.1 * max(reset, sm(2.980, 3.060, t)));
// the resolver: a dense core in a ring, its dotted paths out to the three it asks
v += blob(p, vec2(158.0, 341.900), 15.0, 1.1);
v += 0.55 * exp(-pow(length((p - vec2(158.0, 341.900)) * vec2(1.0, 1.15)) - 27.0, 2.0) / 22.0);
v = max(v, dl(p, vec2(182.0, 317.900), vec2(380.0, 108.700), 0.34));
v = max(v, dl(p, vec2(190.0, 329.900), vec2(318.0, 211.500), 0.34));
v = max(v, dl(p, vec2(194.0, 345.900), vec2(298.500, 361.900), 0.34));
v = max(v, dl(p, vec2(140.0, 313.900), vec2(67.0, 212.800), 0.34));
// the laptop: a screen with a rim and lines on it, a base under it; a line lights once the address arrives
{
float lc = col - 5.0; float lr = cell - 16.0;
if (lr >= 0.0 && lr <= 4.0 && lc >= 0.0 && lc <= 8.0) {
float rim = (lc < 0.5 || lc > 7.5 || lr < 0.5 || lr > 3.5) ? 1.0 : 0.0;
float on = max(reset, sm(3.880, 3.980, t));
float a = 0.16;
if (abs(lr - 1.0) < 0.5 && lc > 0.5 && lc < 6.5) a = 0.3 + 0.8 * on; // the page it asked for, once it has the address
if (abs(lr - 2.0) < 0.5 && lc > 0.5 && lc < 4.5) a = 0.3 + 0.45 * on;
v = max(v, rim > 0.5 ? 0.62 : a);
}
if (abs(lr - 5.0) < 0.5 && lc >= -1.0 && lc <= 9.0) v = max(v, abs(lc - 4.0) < 0.5 ? 0.25 : 0.7);
}
// the resolver's questions and the answers
{
float u = clamp((t - 0.300) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(67.0, 212.800), vec2(158.0, 341.900), ease(u)); vec2 dir = normalize(vec2(158.0, 341.900) - vec2(67.0, 212.800));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 0.740) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(158.0, 341.900), vec2(380.0, 108.700), ease(u)); vec2 dir = normalize(vec2(380.0, 108.700) - vec2(158.0, 341.900));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 1.220) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(380.0, 108.700), vec2(158.0, 341.900), ease(u)); vec2 dir = normalize(vec2(158.0, 341.900) - vec2(380.0, 108.700));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 1.660) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(158.0, 341.900), vec2(318.0, 211.500), ease(u)); vec2 dir = normalize(vec2(318.0, 211.500) - vec2(158.0, 341.900));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 2.140) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(318.0, 211.500), vec2(158.0, 341.900), ease(u)); vec2 dir = normalize(vec2(158.0, 341.900) - vec2(318.0, 211.500));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 2.580) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(158.0, 341.900), vec2(298.500, 361.900), ease(u)); vec2 dir = normalize(vec2(298.500, 361.900) - vec2(158.0, 341.900));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 3.060) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(298.500, 361.900), vec2(158.0, 341.900), ease(u)); vec2 dir = normalize(vec2(158.0, 341.900) - vec2(298.500, 361.900));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
{
float u = clamp((t - 3.500) / 0.400, 0.0, 1.0);
if (u > 0.0 && u < 1.0) {
vec2 c = mix(vec2(158.0, 341.900), vec2(67.0, 212.800), ease(u)); vec2 dir = normalize(vec2(67.0, 212.800) - vec2(158.0, 341.900));
float k = sm(0.0, 0.08, u) * (1.0 - sm(0.92, 1.0, u));
v += blob(p, c, 9.0, 1.2 * k) + blob(p, c - dir * 14.0, 6.5, 0.5 * k) + blob(p, c - dir * 25.0, 4.5, 0.25 * k);
}
}
// the resolver flashes as each answer comes back
v += blob(p, vec2(158.0, 341.900), 18.0, 0.45 * (exp(-pow(t - 0.7, 2.0) / 0.004) + exp(-pow(t - 1.62, 2.0) / 0.004) + exp(-pow(t - 2.54, 2.0) / 0.004) + exp(-pow(t - 3.46, 2.0) / 0.004)));
return v;
}]]>
Kestrel answers the sandbox's name lookups itself. A host on the allow list resolves and the request goes out. Any other name gets no address, so the request never starts.
List hosts in kestrel.yaml at the root of the repo. Wildcards match one level of subdomain.
sandbox :
network :
allow :
- registry.npmjs.org
- pypi.org
- "*.githubusercontent.com" The allow list applies to every run on the repo, and to sandboxes you make with the SDK for that repo.
Most builds only need a package registry. Use a preset instead of listing hosts.
Preset Allows npmregistry.npmjs.org, registry.yarnpkg.com pythonpypi.org, files.pythonhosted.org goproxy.golang.org, sum.golang.org githubgithub.com, *.githubusercontent.com
sandbox :
network :
presets : [ npm , github ]
A name that isn't on the list doesn't resolve, so the request fails at once with a DNS error rather than hanging until a timeout. The trace marks it, so you can see which host a build was reaching for.
$ curl https://example.com
curl: (6) Could not resolve host: example.com
kestrel: example.com is not on the allow list for acme/web-app Warning Allowing a host lets the agent send data to it, not only fetch from it. Keep the list to registries and mirrors you trust.