forked from cloudflare/workerd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
workerd.capnp
854 lines (717 loc) · 36.7 KB
/
workerd.capnp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
# Copyright (c) 2017-2022 Cloudflare, Inc.
# Licensed under the Apache 2.0 license found in the LICENSE file or at:
# https://opensource.org/licenses/Apache-2.0
@0xe6afd26682091c01;
# This file defines the schema for configuring the workerd runtime.
#
# A config file can be written as a `.capnp` file that imports this file and then defines a
# constant of type `Config`. Alternatively, various higher-level tooling (e.g. wrangler) may
# generate configs for you, outputting a binary Cap'n Proto file.
#
# To start a server with a config, do:
#
# workerd serve my-config.capnp constantName
#
# You can also build a new self-contained binary which combines the `workerd` binary with your
# configuration and all your source code:
#
# workerd compile my-config.capnp constantName -o my-server-bin
#
# This binary can then be run stand-alone.
#
# A common theme in this configuration is capability-based design. We generally like to avoid
# giving a Worker the ability to access external resources by name, since this makes it hard
# to see and restrict what each Worker can access. Instead, the default is that a Worker has
# access to no privileged resources at all, and you must explicitly declare "bindings" to give
# it access to specific resources. A binding gives the Worker a JavaScript API object that points
# to a specific resource. This means that by changing config alone, you can fully controll which
# resources an Worker connects to. (You can even disallow access to the public internet, although
# public internet access is granted by default.)
#
# This config format is fairly powerful, allowing you to do things like define a TLS-terminating
# reverse proxy server without using any actual JavaScript code. However, you should not be
# afraid to fall back to code for anything the config cannot express, as Workers are very fast
# to execute!
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("workerd::server::config");
$Cxx.allowCancellation;
struct Config {
# Top-level configuration for a workerd instance.
services @0 :List(Service);
# List of named services defined by this server. These names are private; they are only used
# to refer to the services from elsewhere in this config file, as well as for logging and the
# like. Services are not reachable until you configure some way to make them reachable, such
# as via a Socket.
#
# If you do not define any service called "internet", one is defined implicitly, representing
# the ability to access public internet servers. An explicit definition would look like:
#
# ( name = "internet",
# network = (
# allow = ["public"], # Allows connections to publicly-routable addresses only.
# tlsOptions = (trustBrowserCas = true)
# )
# )
#
# The "internet" service backs the global `fetch()` function in a Worker, unless that Worker's
# configuration specifies some other service using the `globalOutbound` setting.
sockets @1 :List(Socket);
# List of sockets on which this server will listen, and the services that will be exposed
# through them.
v8Flags @2 :List(Text);
# List of "command-line" flags to pass to V8, like "--expose-gc". We put these in the config
# rather than on the actual command line because for most use cases, managing these via the
# config file is probably cleaner and easier than passing on the actual CLI.
#
# WARNING: Use at your own risk. V8 flags can have all sorts of wild effects including completely
# breaking everything. V8 flags also generally do not come with any guarantee of stability
# between V8 versions. Most users should not set any V8 flags.
extensions @3 :List(Extension);
# Extensions provide capabilities to all workers. Extensions are usually prepared separately
# and are late-linked with the app using this config field.
}
# ========================================================================================
# Sockets
struct Socket {
name @0 :Text;
# Each socket has a unique name which can be used on the command line to override the socket's
# address with `--socket-addr <name>=<addr>` or `--socket-fd <name>=<fd>`.
address @1 :Text;
# Address/port on which this socket will listen. Optional; if not specified, then you will be
# required to specify the socket on the command line with with `--socket-addr <name>=<addr>` or
# `--socket-fd <name>=<fd>`.
#
# Examples:
# - "*:80": Listen on port 80 on all local IPv4 and IPv6 interfaces.
# - "1.2.3.4": Listen on the specific IPv4 address on the default port for the protocol.
# - "1.2.3.4:80": Listen on the specific IPv4 address and port.
# - "1234:5678::abcd": Listen on the specific IPv6 address on the default port for the protocol.
# - "[1234:5678::abcd]:80": Listen on the specific IPv6 address and port.
# - "unix:/path/to/socket": Listen on a Unix socket.
# - "unix-abstract:name": On Linux, listen on the given "abstract" Unix socket name.
# - "example.com:80": Perform a DNS lookup to determine the address, and then listen on it. If
# this resolves to multiple addresses, listen on all of them.
#
# (These are the formats supported by KJ's parseAddress().)
union {
http @2 :HttpOptions;
https :group {
options @3 :HttpOptions;
tlsOptions @4 :TlsOptions;
}
# TODO(someday): TCP, TCP proxy, SMTP, Cap'n Proto, ...
}
service @5 :ServiceDesignator;
# Service name which should handle requests on this socket.
# TODO(someday): Support mapping different hostnames to different services? Or should that be
# done strictly via JavaScript?
}
# ========================================================================================
# Services
struct Service {
# Defines a named service. Each server has a list of named services. The names are private,
# used to refer to the services within this same config file.
name @0 :Text;
# Name of the service. Used only to refer to the service from elsewhere in the config file.
# Services are not accessible unless you explicitly configure them to be, such as through a
# `Socket` or through a binding from another Worker.
union {
unspecified @1 :Void;
# (This catches when someone forgets to specify one of the union members. Do not set this.)
worker @2 :Worker;
# A Worker!
network @3 :Network;
# A service that implements access to a network. fetch() requests are routed according to
# the URL hostname.
external @4 :ExternalServer;
# A service that forwards all requests to a specific remote server. Typically used to
# connect to a back-end server on your internal network.
disk @5 :DiskDirectory;
# An HTTP service backed by a directory on disk, supporting a basic HTTP GET/PUT. Generally
# not intended to be exposed directly to the internet; typically you want to bind this into
# a Worker that adds logic for setting Content-Type and the like.
}
# TODO(someday): Allow defining a list of middlewares to stack on top of the service. This would
# be a list of Worker names, where each Worker must have a binding called `next`. This
# implicitly creates an inherited worker that wraps this service, with the `next` binding
# pointing to the service itself (or to the next middleware in the stack).
}
struct ServiceDesignator {
# A reference to a service from elsewhere in the config file, e.g. from a service binding in a
# Worker.
#
# In the case that only `name` needs to be specified, then you can provide a raw string wherever
# `ServiceDesignator` is needed. Cap'n proto automatically assumes the string is intended to be
# the value for `name`, since that is the first field. In other words, if you would otherwise
# write something like:
#
# bindings = [(service = (name = "foo"))]
#
# You can write this instead, which is equivalent:
#
# bindings = [(service = "foo")]
name @0 :Text;
# Name of the service in the Config.services list.
entrypoint @1 :Text;
# A modules-syntax Worker can export multiple named entrypoints. `export default {` specifies
# the default entrypoint, whereas `export let foo = {` defines an entrypoint named `foo`. If
# `entrypoint` is specified here, it names an alternate entrypoint to use on the target worker,
# otherwise the default is used.
# TODO(someday): Options to specify which event types are allowed.
# TODO(someday): Allow adding an outgoing middleware stack here (see TODO in Service, above).
}
struct Worker {
union {
modules @0 :List(Module);
# The Worker is composed of ES modules that may import each other. The first module in the list
# is the main module, which exports event handlers.
serviceWorkerScript @1 :Text;
# The Worker is composed of one big script that uses global `addEventListener()` to register
# event handlers.
#
# The value of this field is the raw source code. When using Cap'n Proto text format, use the
# `embed` directive to read the code from an exnternal file:
#
# serviceWorkerScript = embed "worker.js"
inherit @2 :Text;
# Inherit the configuration of some other Worker by its service name. This Worker is a clone
# of the other worker, but various settings can be modified:
# * `bindings`, if specified, overrides specific named bindings. (Each binding listed in the
# derived worker must match the name and type of some binding in the inherited worker.)
# * `globalOutbound`, if non-null, overrides the one specified in the inherited worker.
# * `compatibilityDate` and `compatibilityFlags` CANNOT be modified; they must be null.
# * If the inherited worker defines durable object namespaces, then the derived worker must
# specify `durableObjectStorage` to specify where its instances should be stored. Each
# devived worker receives its own namespace of objects. `durableObjectUniqueKeyModifier`
# must also be specified by derived workers.
#
# This can be useful when you want to run the same Worker in multiple configurations or hooked
# up to different back-ends. Note that all derived workers run in the same isolate as the
# base worker; they differ in the content of the `env` object passed to them, which contains
# the bindings. (When using service workers syntax, the global scope contains the bindings;
# in this case each derived worker runs in its own global scope, though still in the same
# isolate.)
}
struct Module {
name @0 :Text;
# Name (or path) used to import the module.
union {
esModule @1 :Text;
# An ES module file with imports and exports.
#
# As with `serviceWorkerScript`, above, the value is the raw source code.
commonJsModule @2 :Text;
# A common JS module, using require().
text @3 :Text;
# A raw text blob. Importing this will produce a string with the value.
data @4 :Data;
# A raw data blob. Importing this will produce an ArrayBuffer with the value.
wasm @5 :Data;
# A Wasm module. The value is a compiled binary Wasm module file. Importing this will produce
# a `WebAssembly.Module` object, which you can then instantiate.
json @6 :Text;
# Importing this will produce the result of parsing the given text as JSON.
nodeJsCompatModule @7 :Text;
# A Node.js module is a specialization of a commonJsModule that:
# (a) allows for importing Node.js-compat built-ins without the node: specifier-prefix
# (b) exposes the subset of common Node.js globals such as process, Buffer, etc that
# we implement in the workerd runtime.
}
}
compatibilityDate @3 :Text;
compatibilityFlags @4 :List(Text);
# See: https://developers.cloudflare.com/workers/platform/compatibility-dates/
#
# `compatibilityDate` must be specified, unless the Worker inhits from another worker, in which
# case it must not be specified. `compatibilityFlags` can optionally be specified when
# `compatibilityDate` is specified.
bindings @5 :List(Binding);
# List of bindings, which give the Worker access to external resources and configuration
# settings.
#
# For Workers using ES modules syntax, the bindings are delivered via the `env` object. For
# service workers syntax, each binding shows up as a global variable.
struct Binding {
name @0 :Text;
union {
unspecified @1 :Void;
# (This catches when someone forgets to specify one of the union members. Do not set this.)
parameter :group {
# Indicates that the Worker requires a binding of the given type, but it won't be specified
# here. Another Worker can inherit this Worker and fill in this binding.
type @2 :Type;
# Expected type of this parameter.
optional @3 :Bool;
# If true, this binding is optional. Derived workers need not specify it, in which case
# the binding won't be present in the environment object passed to the worker.
#
# When a Worker has any non-optional parameters that haven't been filled in, then it can
# only be used for inheritance; it cannot be invoked directly.
}
text @4 :Text;
# A string.
data @5 :Data;
# An ArrayBuffer.
json @6 :Text;
# A value parsed from JSON.
wasmModule @7 :Data;
# A WebAssembly module. The binding will be an instance of `WebAssembly.Module`. Only
# supported when using Service Workers syntax.
#
# DEPRECATED: Please switch to ES modules syntax instead, and embed Wasm modules as modules.
cryptoKey @8 :CryptoKey;
# A CryptoKey instance, for use with the WebCrypto API.
#
# Note that by setting `extractable = false`, you can prevent the Worker code from accessing
# or leaking the raw key material; it will only be able to use the key to perform WebCrypto
# operations.
service @9 :ServiceDesignator;
# Binding to a named service (possibly, a worker).
durableObjectNamespace @10 :DurableObjectNamespaceDesignator;
# Binding to the durable object namespace implemented by the given class.
#
# In the common case that this refers to a class in the same Worker, you can specify just
# a string, like:
#
# durableObjectNamespace = "MyClass"
kvNamespace @11 :ServiceDesignator;
# A KV namespace, implemented by the named service. The Worker sees a KvNamespace-typed
# binding. Requests to the namespace will be converted into HTTP requests targetting the
# given service name.
r2Bucket @12 :ServiceDesignator;
r2Admin @13 :ServiceDesignator;
# R2 bucket and admin API bindings. Similar to KV namespaces, these turn operations into
# HTTP requests aimed at the named service.
wrapped @14 :WrappedBinding;
# Wraps a collection of inner bindings in a common api functionality.
queue @15 :ServiceDesignator;
# A Queue binding, implemented by the named service. Requests to the
# namespace will be converted into HTTP requests targetting the given
# service name.
fromEnvironment @16 :Text;
# Takes the value of an environment variable from the system. The value specified here is
# the name of a system environment variable. The value of the binding is obtained by invoking
# `getenv()` with that name. If the environment variable isn't set, the binding value is
# `null`.
analyticsEngine @17 :ServiceDesignator;
# A binding for Analytics Engine. Allows workers to store information through Analytics Engine Events.
# workerd will forward AnalyticsEngineEvents to designated service in the body of HTTP requests
# This binding is subject to change and requires the `--experimental` flag
# TODO(someday): dispatch, other new features
}
struct Type {
# Specifies the type of a parameter binding.
union {
unspecified @0 :Void;
# (This catches when someone forgets to specify one of the union members. Do not set this.)
text @1 :Void;
data @2 :Void;
json @3 :Void;
wasm @4 :Void;
cryptoKey @5 :List(CryptoKey.Usage);
service @6 :Void;
durableObjectNamespace @7 :Void;
kvNamespace @8 :Void;
r2Bucket @9 :Void;
r2Admin @10 :Void;
queue @11 :Void;
analyticsEngine @12 : Void;
}
}
struct DurableObjectNamespaceDesignator {
# The type of a Durable Object namespace binding.
className @0 :Text;
# Exported class name that implements the Durable Object.
serviceName @1 :Text;
# The service name of the worker that defines this class. If omitted, the current worker
# is assumed.
#
# Use of this field is discouraged. Instead, when accessing a different Worker's Durable
# Objects, specify a `service` binding to that worker, and have the worker implement an
# appropriate API.
#
# (This is intentionally not a ServiceDesignator because you cannot choose an alternate
# entrypoint here; the class name IS the entrypoint.)
}
struct CryptoKey {
# Parameters to crypto.subtle.importKey().
union {
raw @0 :Data;
hex @1 :Text;
base64 @2 :Text;
# Raw key material, possibly hex or base64-encoded. Use this for symmetric keys.
#
# Hint: `raw` would typically be used with Cap'n Proto's `embed` syntax to embed an
# external binary key file. `hex` or `base64` could do that too but can also be specified
# inline.
pkcs8 @3 :Text;
# Private key in PEM-encoded PKCS#8 format.
spki @4 :Text;
# Public key in PEM-encoded SPKI format.
jwk @5 :Text;
# Key in JSON format.
}
algorithm :union {
# Value for the `algorithm` parameter.
name @6 :Text;
# Just a name, like `AES-GCM`.
json @7 :Text;
# An object, encoded here as JSON.
}
extractable @8 :Bool = false;
# Is the Worker allowed to export this key to obtain the underlying key material? Setting
# this false ensures that the key cannot be leaked by errant JavaScript code; the key can
# only be used in WebCrypto operations.
usages @9 :List(Usage);
# What operations is this key permitted to be used for?
enum Usage {
encrypt @0;
decrypt @1;
sign @2;
verify @3;
deriveKey @4;
deriveBits @5;
wrapKey @6;
unwrapKey @7;
}
}
struct WrappedBinding {
# A binding that wraps a group of (lower-level) bindings in a common API.
moduleName @0 :Text;
# Wrapper module name.
# The module must be an internal one (provided by extension or registered in the c++ code).
# Module will be instantitated during binding initialization phase.
entrypoint @1 :Text = "default";
# Module needs to export a function with a given name (default export gets "default" name).
# The function needs to accept a single `env` argument - a dictionary with inner bindings.
# Function will be invoked during initialization phase and its return value will be used as
# resulting binding value.
innerBindings @2 :List(Binding);
# Inner bindings that will be created and passed in the env dictionary.
# These bindings shall be used to implement end-user api, and are not available to the
# binding consumers unless "re-exported" in wrapBindings function.
}
}
globalOutbound @6 :ServiceDesignator = "internet";
# Where should the global "fetch" go to? The default is the service called "internet", which
# should usually be configured to talk to the public internet.
cacheApiOutbound @11 :ServiceDesignator;
# Where should cache API (i.e. caches.default and caches.open(...)) requests go?
durableObjectNamespaces @7 :List(DurableObjectNamespace);
# List of durable object namespaces in this Worker.
struct DurableObjectNamespace {
className @0 :Text;
# Exported class name that implements the Durable Object.
#
# Changing the class name will not break compatibility with existing storage, so long as
# `uniqueKey` stays the same.
union {
uniqueKey @1 :Text;
# A unique, stable ID associated with this namespace. This could be a GUID, or any other
# string which does not appear anywhere else in the world.
#
# This string is used to ensure that objects of this class have unique identifiers distinct
# from objects of any other class. Object IDs are cryptographically derived from `uniqueKey`
# and validated against it. It is impossible to guess or forge a valid object ID without
# knowing the `uniqueKey`. Hence, if you keep the key secret, you can prevent anyone from
# forging IDs. However, if you don't care if users can forge valid IDs, then it's not a big
# deal if the key leaks.
#
# DO NOT LOSE this key, otherwise it may be difficult or impossible to recover stored data.
ephemeralLocal @2 :Void;
# Instances of this class are ephemeral -- they have no durable storage at all. The
# `state.storage` API will not be present. Additionally, this namespace will allow arbitrary
# strings as IDs. There are no `idFromName()` nor `newUniqueId()` methods; `get()` takes any
# string as a paremeter.
#
# Ephemeral objects are NOT globally unique, only "locally" unique, for some definition of
# "local". For exmaple, on Cloudflare's network, these objects are unique per-colo.
#
# WARNING: Cloudflare Workers currently limits this feature to Cloudflare-internal users
# only, because using them correctly requires deep understanding of Cloudflare network
# topology. We're working on something better for public consuption. Until then for
# "ephemeral" use cases we recommend using regular durable objects and just not storing
# anything. An object that hasn't stored anything will not consume any storage space on
# disk.
}
}
durableObjectUniqueKeyModifier @8 :Text;
# Additional text which is hashed together with `DurableObjectNamespace.uniqueKey`. When using
# worker inheritance, each derived worker must specify a unique modifier to ensure that its
# Durable Object instances have unique IDs from all other workers inheriting the same parent.
#
# DO NOT LOSE this value, otherwise it may be difficult or impossible to recover stored data.
durableObjectStorage :union {
# Specifies where this worker's Durable Objects are stored.
none @9 :Void;
# Default. The worker has no Durable Objects. `durableObjectNamespaces` must be empty, or
# define all namespaces as `ephemeralLocal`, or this must be an abstract worker (meant to be
# inherited by other workers, who will specify `durableObjectStorage`).
inMemory @10 :Void;
# The `state.storage` API stores in-memory only. All stored data will persist for the
# lifetime of the process, but will be lost upon process exit.
#
# Individual objects will still shut down when idle as normal -- only data stored with the
# `state.storage` interface is persistent for the lifetime of the process.
#
# This mode is intended for local testing purposes.
localDisk @12 :Text;
# ** EXPERIMENTAL; SUBJECT TO BACKWARDS-INCOMPATIBLE CHANGE **
#
# Durable Object data will be stored in a directory on local disk. This field is the name of
# a service, which must be a DiskDirectory service. For each Durable Object class, a
# subdirectory will be created using `uniqueKey` as the name. Within the directory, one or
# more files are created for each object, with names `<id>.<ext>`, where `.<ext>` may be any of
# a number of different extensions depending on the storage mode. (Currently, the main storage
# is a file with the extension `.sqlite`, and in certain situations extra files with the
# extensions `.sqlite-wal`, and `.sqlite-shm` may also be present.)
}
# TODO(someday): Support distributing objects across a cluster. At present, objects are always
# local to one instance of the runtime.
}
struct ExternalServer {
# Describes the ability to talk to a specific server, typically a back-end server available
# on the internal network.
#
# When a Worker contains a service binding that points to an ExternalServer, *all* fetch()
# calls on that binding will be delivered to that server, regardless of whether the hostname
# or protocol specified in the URL actually match the hostname or protocol used by the actual
# server. Typically, a Worker implementing a reverse proxy would use this to forward a request
# to a back-end application server. Such a back-end typically does not have a real public
# hostname, since it is only reachable through the proxy, but the requests forwarded to it will
# keep the hostname that was on the original request.
#
# Note that this also implies that regardless of whether the original URL was http: or https:,
# the request will be delivered to the target server using the protocol specified below. A
# header like `X-Forwarded-Proto` can be used to pass along the original protocol; see
# `HttpOptions`.
address @0 :Text;
# Address/port of the server. Optional; if not specified, then you will be required to specify
# the address on the command line with with `--external-addr <name>=<addr>`.
#
# Examples:
# - "1.2.3.4": Connect to the given IPv4 address on the protocol's default port.
# - "1.2.3.4:80": Connect to the given IPv4 address and port.
# - "1234:5678::abcd": Connect to the given IPv6 address on the protocol's default port.
# - "[1234:5678::abcd]:80": Connect to the given IPv6 address and port.
# - "unix:/path/to/socket": Connect to the given Unix Domain socket by path.
# - "unix-abstract:name": On Linux, connect to the given "abstract" Unix socket name.
# - "example.com:80": Perform a DNS lookup to determine the address, and then connect to it.
#
# (These are the formats supported by KJ's parseAddress().)
union {
http @1 :HttpOptions;
# Talk to the server over unencrypted HTTP.
https :group {
# Talk to the server over encrypted HTTPS.
options @2 :HttpOptions;
tlsOptions @3 :TlsOptions;
certificateHost @4 :Text;
# If present, expect the host to present a certificate authenticating it as this hostname.
# If `certificateHost` is not provided, then the certificate is checked against `address`.
}
# TODO(someday): Cap'n Proto RPC
}
}
struct Network {
# Describes the ability to talk to a network.
#
# This is commonly used to define the "internet" service which is the default `globalOutbound`
# for all Workers. To prevent SSRF, by default Workers will not be permitted to reach internal
# network addresses using global fetch(). It's recommended that you create ExternalServer
# bindings instead to grant access to specific servers. However, if you really want to, you
# can configure a service that grants arbitrary internal network access, like:
#
# ( name = "internalNetwork",
# network = (
# allow = ["public", "private"],
# )
# )
allow @0 :List(Text) = ["public"];
deny @1 :List(Text);
# Specifies which network addresses the Worker will be allowed to connect to, e.g. using fetch().
# The default allows publicly-routable IP addresses only, in order to prevent SSRF attacks.
#
# The allow and deny lists specify network blocks in CIDR notation (IPv4 and IPv6), such as
# "192.0.2.0/24" or "2001:db8::/32". Traffic will be permitted as long as the address
# matches at least one entry in the allow list and none in the deny list.
#
# In addition to IPv4 and IPv6 CIDR notation, several special strings may be specified:
# - "private": Matches network addresses that are reserved by standards for private networks,
# such as "10.0.0.0/8" or "192.168.0.0/16". This is a superset of "local".
# - "public": Opposite of "private".
# - "local": Matches network addresses that are defined by standards to only be accessible from
# the local machine, such as "127.0.0.0/8" or Unix domain addresses.
# - "network": Opposite of "local".
# - "unix": Matches all Unix domain socket addresses. (In the future, we may support specifying a
# glob to narrow this to specific paths.)
# - "unix-abstract": Matches Linux's "abstract unix domain" addresses. (In the future, we may
# support specifying a glob.)
#
# In the case that the Worker specifies a DNS hostname rather than a raw address, these rules are
# used to filter the addresses returned by the lookup. If none of the returned addresses turn
# out to be permitted, then the system will behave as if the DNS entry did not exist.
#
# (The above is exactly the format supported by kj::Network::restrictPeers().)
tlsOptions @2 :TlsOptions;
}
struct DiskDirectory {
# Configures access to a directory on disk. This is a type of service which will expose an HTTP
# interface to the directory content.
#
# This is very bare-bones, generally not suitable for serving a web site on its own. In
# particular, no attempt is made to guess the `Content-Type` header. You normally would wrap
# this in a Worker that fills in the metadata in the way you want.
#
# A GET request targetting a directory (rather than a file) will return a basic JSAN directory
# listing like:
#
# [{"name":"foo","type":"file"},{"name":"bar","type":"directory"}]
#
# Possible "type" values are "file", "directory", "symlink", "blockDevice", "characterDevice",
# "namedPipe", "socket", "other".
#
# `Content-Type` will be `application/octet-stream` for files or `application/json` for a
# directory listing. Files will have a `Content-Length` header, directories will not. Symlinks
# will be followed (but there is intentionally no way to create one, even if `writable` is
# `true`), and treated according to the type of file they point to. The other inode types cannot
# be opened; trying to do so will produce a "406 Not Acceptable" error (on the theory that there
# is no acceptable format for these, regardless of what the client says it accepts).
#
# `HEAD` requests are properly optimized to perform a stat() without actually opening the file.
path @0 :Text;
# The filesystem path of the directory. If not specified, then it must be specified on the
# command line with `--directory-path <service-name>=<path>`.
#
# Relative paths are interpreted relative to the current directory where the server is executed,
# NOT relative to the config file. So, you should usually use absolute paths in the config file.
writable @1 :Bool = false;
# Whether to support PUT requests for writing. A PUT will write to a temporary file which
# is atomically moved into place upon successful completion of the upload. Parent directories are
# created as needed.
allowDotfiles @2 :Bool = false;
# Whether to allow access to files and directories whose name starts with '.'. These are made
# inaccessible by default since they very often store metadata that is not meant to be served,
# e.g. a git repository or an `.htaccess` file.
#
# Note that the special links "." and ".." will never be accessible regardless of this setting.
}
# ========================================================================================
# Protocol options
struct HttpOptions {
# Options for using HTTP (as a client or server). In particular, this specifies behavior that is
# important in the presence of proxy servers, whether forward or reverse.
style @0 :Style = host;
enum Style {
host @0;
# Normal HTTP. The request line contains only the path, and the separate `Host` header
# specifies the hostname.
proxy @1;
# HTTP proxy protocol. The request line contains a full URL instead of a path. No `Host`
# header is required. This is the protocol used by HTTP forward proxies. This allows you to
# implement such a proxy as a Worker.
}
forwardedProtoHeader @1 :Text;
# If specified, then when the given header is present on a request, it specifies the protocol
# ("http" or "https") that was used by the original client. The request URL reported to the
# Worker will reflect this protocol. Otherwise, the URL will reflect the actual physical protocol
# used by the server in receiving the request.
#
# This option is useful when this server sits behind a reverse proxy that performs TLS
# termination. Typically such proxies forward the original protocol in a header named something
# like "X-Forwarded-Proto".
#
# This setting is ignored when `style` is `proxy`.
cfBlobHeader @2 :Text;
# If set, then the `request.cf` object will be encoded (as JSON) into / parsed from the header
# with this name. Otherwise, it will be discarded on send / `undefined` on receipt.
injectRequestHeaders @3 :List(Header);
# List of headers which will be automatically injected into all requests. This can be used
# e.g. to add an authorization token to all requests when using `ExternalServer`. It can also
# apply to incoming requests received on a `Socket` to modify the headers that will be delivered
# to the app. Any existing header with the same name is removed.
injectResponseHeaders @4 :List(Header);
# Same as `injectRequestHeaders` but for responses.
struct Header {
name @0 :Text;
# Case-insensitive.
value @1 :Text;
# If null, the header will be removed.
}
# TODO(someday): When we support TCP, include an option to deliver CONNECT requests to the
# TCP handler.
}
struct TlsOptions {
# Options that apply when using TLS. Can apply on either the client or the server side, depending
# on the context.
#
# This is based on KJ's TlsContext::Options.
keypair @0 :Keypair;
# The default private key and certificate to use. Optional when acting as a client.
struct Keypair {
privateKey @0 :Text;
# Private key in PEM format. Supports PKCS8 keys as well as "traditional format" RSA and DSA
# keys.
#
# Remember that you can use Cap'n Proto's `embed` syntax to reference an external file.
certificateChain @1 :Text;
# Certificate chain in PEM format. A chain can be constructed by concatenating multiple
# PEM-encoded certificates, starting with the leaf certificate.
}
# TODO(someday): Support SNI-based keypair selection? Is a hostname -> keypair map good enough?
# Does it need to support wildcards? Maybe we should just let you provide a pile of certs and
# we can figure out which hosts each one matches?
requireClientCerts @1 :Bool = false;
# If true, then when acting as a server, incoming connections will be rejected unless they bear
# a certificate signed by one of the trusted CAs.
#
# Typically, when using this, you'd set `trustBrowserCas = false` and list a specific private CA
# in `trustedCertificates`.
trustBrowserCas @2 :Bool = false;
# If true, trust certificates which are signed by one of the CAs that browsers normally trust.
# You should typically set this true when talking to the public internet, but you may want to
# set it false when talking to servers on your internal network.
trustedCertificates @3 :List(Text);
# Additional CA certificates to trust, in PEM format. Remember that you can use Cap'n Proto's
# `embed` syntax to read the certificates from other files.
minVersion @4 :Version = goodDefault;
# Minimum TLS version that will be allowed. Generally you should not override this unless you
# have unusual backwards-compatibility needs.
enum Version {
goodDefault @0;
# A good default chosen by the code maintainers. May change over time.
ssl3 @1;
tls1Dot0 @2;
tls1Dot1 @3;
tls1Dot2 @4;
tls1Dot3 @5;
}
cipherList @5 :Text;
# OpenSSL cipher list string. The default is a curated list designed to be compatible with
# almost all software in current use (specifically, based on Mozilla's "intermediate"
# recommendations). The defaults will change in future versions of this software to account
# for the latest cryptanalysis.
#
# Generally you should only specify your own `cipherList` if:
# - You have extreme backwards-compatibility needs and wish to enable obsolete and/or broken
# algorithms.
# - You need quickly to disable an algorithm recently discovered to be broken.
}
# ========================================================================================
# Extensions
struct Extension {
# Additional capabilities for workers.
modules @0 :List(Module);
# List of javascript modules provided by the extension.
# These modules can either be imported directly as user-level api (if not marked internal)
# or used to define more complicated workerd constructs such as wrapped bindings and events.
struct Module {
# A module extending workerd functionality.
name @0 :Text;
# Full js module name.
internal @1 :Bool = false;
# Internal modules can be imported by other extension modules only and not the user code.
esModule @2 :Text;
# Raw source code of ES module.
}
}