Skip to content
File

Blob: src/workerd/server/channel-token.capnp

4.1 KB
1# Copyright (c) 2025 Cloudflare, Inc.
2# Licensed under the Apache 2.0 license found in the LICENSE file or at:
3# https://opensource.org/licenses/Apache-2.0
4 
5@0xc086f616deb649e5;
6 
7using Cxx = import "/capnp/c++.capnp";
8$Cxx.namespace("workerd::server");
9$Cxx.allowCancellation;
10 
11using Frankenvalue = import "/workerd/io/frankenvalue.capnp".Frankenvalue;
12 
13struct ChannelToken {
14 # Internal structure of a channel token in workerd, as returned by
15 # {SubrequestChannel,ActorClassChannel}::getToken().
16 #
17 # For RPC tokens, this structure is encoded as a "packed" capnp message and then AES-GCM
18 # encrypted using a secret service key and random IV to form the final token. The full token
19 # contains, in order:
20 # * 4-byte magic number: 0x821dad26 (little-endian encoded)
21 # * 12-byte IV
22 # * 16-byte key ID (prefix of SHA-256 hash of secret key, not encrypted)
23 # * ciphertext
24 # * 16-byte MAC (covering ciphertext and the first 32 bytes as AAD)
25 #
26 # The encryption (particularly the MAC) is important in order to ensure that someone speaking to
27 # workerd over RPC cannot trivially invoke an arbitrary service with arbitrary props by simply
28 # presenting a channel token.
29 #
30 # As of this writing, for RPC tokens, the secret key is generated randomly at process startup.
31 # This means that RPC tokens are only usable within the same workerd process that created them,
32 # which also has the side effect of meaning there is no backwards-compatibilty concern. The
33 # format is likely to change in the future once we figure out more how it should actually be used.
34 #
35 # The 16-byte key ID is included to enable routing. Hypothetically, if workerd processes were to
36 # register their key IDs in some lookup service, then based on the key ID you could find an
37 # appropriate workerd instance to connect to to use this token. As of this writing, this is still
38 # speculative.
39 #
40 # For storage tokens -- which are only permitted today with the experimental
41 # allow_irrevocable_stub_storage compat flag -- the format is:
42 # * 4-byte magic number: 0x9082806d (little-endian encoded)
43 # * plaintext ("packed" ChannelToken)
44 #
45 # There is no encryption in this case. This format is experimental and will be replaced in the
46 # future with a different kind of token that refers into some sort of token grant table.
47 
48 const rpcTokenMagic :UInt32 = 0x821dad26;
49 const storageTokenMagic :UInt32 = 0x9082806d;
50 
51 type @0 :Type;
52 # What type of channel does this point to? This is encoded as a safety measure. In normal
53 # operation the envelope containing the token always knows what type it is meant to be, but we
54 # want to prevent any possible shenanigans from someone taking a channel token of one type and
55 # trying to stuff it in an envelope for a different type.
56 
57 enum Type {
58 subrequest @0; # token for IoChannelFactory::SubrequestChannel
59 actorClass @1; # token for IoChannelFactory::ActorClassChannel
60 }
61 
62 name @1 :Text;
63 # Name of the service in the workerd config's services list.
64 
65 entrypoint @2 :Text;
66 # Name of the entrypoint the channel points at. For subrequest channels this must be a
67 # WorkerEntrypoint derivative (or plain object implementing `ExportedHandlers`). For actor class
68 # channels this must be a `DurableObject` implementation.
69 
70 props @3 :Frankenvalue;
71 
72 struct FrankenvalueCapTable {
73 # CapTable representation for `ChannelToken.props`.
74 
75 caps @0 :List(Cap);
76 
77 struct Cap {
78 union {
79 unknown @0 :Void;
80 # Dummy default value, never appears in practice.
81 
82 subrequestChannel @1 :Data;
83 actorClassChannel @2 :Data;
84 # Nested capabilities are represented using fully encoded channel tokens themselves (rather
85 # than ChannelToken capnp structs) for a couple reasons:
86 # 1. This simplifies the abstractions needed when encoding a channel token -- just call
87 # getToken() on any nested channels.
88 # 2. Channel tokens may be tied to the particular workerd instance that they came from, and
89 # cannot be decoded on other instances, but I suspect we will eventually want to support
90 # props containing capabilities pointing to other workerd instances.
91 }
92 }
93 }
94}