16:01:35 <Shelikhoo[mds]> #startmeeting tor anti-censorship meeting
16:01:35 <MeetBot> Meeting started Thu Aug 20 16:01:35 2026 UTC.  The chair is Shelikhoo[mds]. Information about MeetBot at https://wiki.debian.org/MeetBot.
16:01:35 <MeetBot> Useful Commands: #action #agreed #help #info #idea #link #topic.
16:01:35 <Shelikhoo[mds]> here is our meeting pad: https://pad.riseup.net/p/r.9574e996bb9c0266213d38b91b56c469
16:01:35 <Shelikhoo[mds]> editable link available on request
16:01:50 <onyinyang[mds]> Hello!
16:01:52 <meskio[mds]> hello
16:02:06 <Shelikhoo[mds]> hi~
16:02:27 <cohosh> hi!
16:03:20 <Shelikhoo[mds]> I didn't see any new discussion topic, but we do have a reading group this week
16:03:52 <cohosh> do we need to discuss the snowflake broker deployment?
16:04:02 <cohosh> i saw an announcement last week on that topic
16:04:05 <Shelikhoo[mds]> we can discuss if needed
16:04:26 <Shelikhoo[mds]> yes,
16:04:28 <Shelikhoo[mds]> I have finished running usermode probetest on broker. I will adjust nginx to route traffic to it next Tuesday (https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/work_items/40551)
16:04:28 <cohosh> i saw you opened an issue for the new probetest deployment this morning
16:04:51 <Shelikhoo[mds]> yes, sorry it took a while
16:05:03 <cohosh> oh no problem, there's no rush
16:05:08 <Shelikhoo[mds]> and the new version of probetest is already running at broker
16:05:21 <cohosh> my question was on plans for the broker update
16:05:31 <Shelikhoo[mds]> but did not receive traffic as of now, since nginx config change is not applied
16:05:46 <Shelikhoo[mds]> I intent to switch it on next Tuesday
16:05:51 <cohosh> and whether it will include just the NAT bucket changes or also the proxy rate limiting feature
16:06:21 <cohosh> as in, the broker binary itself
16:06:34 <Shelikhoo[mds]> I think we should make a single deployment that includes both features, but happy to change that if necessary
16:06:54 <cohosh> ok that sounds good to me
16:07:00 <cohosh> here are the relevant MRs:
16:07:04 <cohosh> https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/727
16:07:18 <cohosh> https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/merge_requests/728
16:07:49 <cohosh> that proxy rate limiting feature i am more nervous about because i think it will impact the proxy pool size and capacity
16:08:11 <cohosh> so i want to be around and watching it when we deploy it so we can update the poll interval, which is currently still manual
16:08:36 <cohosh> so just requesting a heads up whenever that deployment happens
16:08:59 <cohosh> that's it from me
16:09:02 <Shelikhoo[mds]> yes! I will let you know before I actually deploy it.
16:09:18 <cohosh> thank you!
16:09:25 <Shelikhoo[mds]> nice!
16:10:28 <Shelikhoo[mds]> okay we can start the reading group for the paper
16:10:33 <Shelikhoo[mds]> https://www.petsymposium.org/popets/2026/popets-2026-0096.pdf
16:10:42 <Shelikhoo[mds]> CensorLess: Cost-Efficient Censorship Circumvention
16:10:43 <Shelikhoo[mds]> Through Serverless Cloud Functions
16:11:09 <Shelikhoo[mds]> anyone wish to share a summary of the paper?
16:11:21 <cohosh> i have one prepared that i can share
16:11:54 <Shelikhoo[mds]> nice!
16:12:04 <cohosh> also, DayeonKang[mds], one of the authors is here :)
16:12:07 <cohosh> welcome!
16:12:13 <meskio[mds]> <3
16:12:34 <DayeonKang[mds]> Hello! Thank you for introducing me :)
16:12:35 <cohosh> i'm cecylia btw
16:13:21 <cohosh> ok <summary>
16:13:32 <cohosh> CensorLess is a cloud-based circumvention system that uses serverless computing, also called FaaS) to run ultra lightweight HTTP proxies.
16:13:35 <cohosh> It is offered as a successor to SpotProxy, https://censorbib.nymity.ch/#Kon2024b, with simpler deployment and migration, and lower estimated costs.
16:13:38 <cohosh> The client runs a local proxy, the concept of which is similar to how a Tor PT client process works. The CensorLess local proxy repackages requests as HTTP requests to an initial proxy URL that is obtained out of band.
16:13:42 <cohosh> The local proxy then passes HTTP responses back to the client application.
16:13:44 <cohosh> This local proxy also watches for migration tags that indicate a change to the proxy URL. This is distinct from IP rotation, which happens automatically by the cloud provider, without the need to change the URL.
16:13:48 <cohosh> The system can function as either an HTTP proxy, or in private mode as an HTTPS tunnel.
16:13:51 <cohosh> It is possible to use domain fronting techniques with AWS Lambda, https://docs.aws.amazon.com/lambda/latest/dg/welcome.html, one FaaS provider, with some interesting properties: the client can use a different front for each HTTP request, and the fronts do not have to be valid URLs, they just need to match the permitted cloud domain pattern.
16:13:56 <cohosh> </summary>
16:14:32 <Shelikhoo[mds]> nice! thanks for the summary!
16:15:17 <meskio[mds]> is a pretty nice paper, the idea is pretty cool and it looks like is not that expensive to run (compared with many other cloud services)
16:16:05 <meskio[mds]> the downside compared to spotproxy is that in our usecase we would need to develop a new PT for it, I dont' think we can use it direclty with any existing PTs, as we could with spotproxy for snowflake
16:16:12 <cohosh> yeah i really enjoyed reading this paper, and i think there is potential for us to use this with some of our tools
16:16:24 <Shelikhoo[mds]> actually it can be used as a signaling channel
16:16:31 <Shelikhoo[mds]> like how it was used in geph
16:16:32 <Shelikhoo[mds]> https://github.com/geph-official/geph5/blob/master/binaries/geph5-client/src/broker/aws_lambda.rs
16:16:35 <cohosh> meskio[mds]: i was thinking this would be a lower cost and simpler alternative to SQS rendezvous
16:16:49 <cohosh> i wrote up some analysis here: https://gitlab.torproject.org/tpo/research/-/work_items/19
16:16:59 <meskio[mds]> yes, using AWS domain fronting, makes sense
16:17:04 <meskio[mds]> and looks easier to set up
16:17:44 <dcf1> my immediate take is that the obvious thing to do is to make a meek-like tunnel
16:17:53 <meskio[mds]> I guess for this we will not need function rotation
16:17:55 <meskio[mds]> so way simpler to set up
16:18:12 <dcf1> just like meek had php/wsgi/appenengine alternatives for the reflector, e.g., https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/meek/-/tree/v0.38.0/php
16:18:19 <cohosh> dcf1: yeah! that was my other thought, that as a standalone PT it would be a nice alternative to meek
16:18:33 <meskio[mds]> totally
16:18:40 <cohosh> but on that note, i have some clarification questions:
16:18:42 <dcf1> there could be a lambda reflector (even if it maybe cannot be made 100% compatible because of serverless APIs or whatever)
16:19:01 <dcf1> i have a lot of questions and observations too, cohosh go ahead
16:19:44 <cohosh> - do we need to do migration if domain fronting is an option?
16:20:12 <cohosh> getting that initial serverless URL into the hands of users is tricky with the migration feature
16:20:27 <cohosh> or is there a static initial URL that remains the same even if the migration feature is on?
16:20:48 <dcf1> and the initial serverless URL, the paper seems to contradict itself on that point, DayeonKang[mds] maybe you can clarify
16:20:52 <dcf1> 4.4 "the local proxy server is preconfigured with an initial serverless bridge URL"
16:20:55 <dcf1> but
16:20:58 <dcf1> 4.5 "upon registration, the operator randomly selects the next serverless bridge from the available pool and allocates it to the client"
16:21:05 <Shelikhoo[mds]> there are actually 2 ways to use aws lambda, the first way is use xxxxxxx.lambda-url.eu-west-1.on.aws , which basically supports all http request
16:21:24 <Shelikhoo[mds]> and can be used as an drop in replacement for any reflector
16:21:41 <dcf1> it's clear there is some kind of initial rendezvous/bootstrapping step, which is necessary because the operator needs to know which clients are live in order to maintain the client–function mapping DB, and also for client and VPS to exchange keys in private mode
16:21:46 <DayeonKang[mds]> In our experiment, we used the static serverless URL address for the initial step, and migrate bridges from the same URL.
16:21:52 <Shelikhoo[mds]> however it would require domain fronting to avoid block by sni
16:21:59 <dcf1> does the initial serverless URL also come out of this rendezvous process, or is it preconfigured as stated?
16:22:18 <Shelikhoo[mds]> the other choice is to call aws domain directly
16:22:57 <Shelikhoo[mds]> like lambda.us-west-1.amazonaws.com
16:23:16 <Shelikhoo[mds]> which do not include user id in sni
16:23:35 <Shelikhoo[mds]> however requires a custom calling convention
16:23:54 <dcf1> Shelikhoo[mds]: that's interesting. From the source code, censorless seems to be using the first option e.g. 'https://7we6n6kmkv7uc6p7zvvjntkmqa0zelbp.lambda-url.us-east-1.on.aws/' https://bitbucket.org/umass-lab/censorless/src/842c2e4458042d210c15f516fdc5fbb902a6f315/censorless-vanilla/local-proxy/src/proxy.ts#lines-168
16:24:30 <cohosh> Shelikhoo[mds]: thanks for those details, so that second option would be to use the AWS API if i'm understanding that right, like how we do with SQS
16:24:37 <Shelikhoo[mds]> dcf1: My tool and I think geph uses the second option: https://github.com/xiaokangwang/lambdaProxy/blob/master/cli/forwarder.go
16:24:45 <dcf1> Shelikhoo[mds]: wait, that is very intriguing, if there's a way of accessing lambda that does not put the user ID in the domain name, even if it requires different techniques to access
16:24:50 <Shelikhoo[mds]> https://github.com/geph-official/geph5/blob/master/binaries/geph5-client/src/broker/aws_lambda.rs
16:24:57 <dcf1> hmm very interesting
16:25:35 <cohosh> yeah that's cool
16:25:41 <dcf1> DayeonKang[mds]: okay, so the experimental evaluation was not complete in that sense, it still had a static preconfigured URL that might be blocked in practice
16:26:05 <dcf1> DayeonKang[mds]: what about the private mode key exchange? is that in the source code? did you just do it manually for your experiments?
16:26:22 <dcf1> (cohosh sorry if I'm intruding on your sequence of questions)
16:26:40 <DayeonKang[mds]> Here's the private mode code: https://bitbucket.org/umass-lab/censorless/src/main/censorless-private/
16:26:43 <cohosh> nope you're good :)
16:26:44 <dcf1> (DayeonKang[mds] some of my questions might be better offline and I can contact you separately)
16:26:59 <DayeonKang[mds]> sounds godd!
16:27:37 <Shelikhoo[mds]> cohosh: yes, it would like how sqs works
16:28:01 <DayeonKang[mds]> I was thinking of how to distribute initial serverless URL address becuase of the privacy. Initial thought on the paper was providing temporary urls, which are changing for each users, by embedding in the program
16:28:27 <DayeonKang[mds]> s/users/user/
16:28:37 <DayeonKang[mds]> * sounds good!
16:30:00 <Shelikhoo[mds]> as for the cost, I think the traffic cost is high enough for AWS in general, we might wants to use it as a signaling channel instead of payload channel
16:30:53 <Shelikhoo[mds]> since the aws egress cost is a little high for free proxy services
16:30:58 <dcf1> cohosh's investigation at https://gitlab.torproject.org/tpo/research/-/work_items/19 says lambda is only 50% of the cost of SQS per request, and SQS is already a non-trivial cost even though it's used only for Snowflake rendezvous, not the main data channel
16:31:09 <cohosh> DayeonKang[mds]: something like push notifications could be good for this kind of scenario: where there is changing initial connection information that only requires unidirectional information from the tool operators to the client
16:31:15 <meskio[mds]> I got the impresion from the paper that they charge per requests and not per traffic, but maybe I missunderstood
16:31:34 <cohosh> dcf1: well, the per API use cost is 50%, SQS uses a lot more API calls for each rendezvous message
16:31:41 <Shelikhoo[mds]> aws charges per request and per traffic
16:31:45 <dcf1> oh
16:31:49 <Shelikhoo[mds]> aws lambda charges per request and per traffic
16:32:24 <cohosh> yeah that was another question i had, which is how much the GPS-second cost factors into using AWS lambda
16:32:40 <Shelikhoo[mds]> https://aws.amazon.com/lambda/pricing/
16:33:36 <Shelikhoo[mds]> The difference with meek is that when the lambda function is idle waiting for server response, the cost for compute is still ticking
16:33:50 <Shelikhoo[mds]> and it will not accept another request to process
16:33:54 <cohosh> Shelikhoo[mds]: by charing per traffic, you mean this compute cost, right?
16:34:17 <cohosh> s/charing/charging
16:34:33 <Shelikhoo[mds]> cohosh: compute cost + traffic cost stacked together
16:34:34 <cohosh> i'm curious if that is negligible compared to the per request cost
16:34:40 <Shelikhoo[mds]> Data Transfer
16:34:40 <Shelikhoo[mds]> Data transferred “in” to and “out” of your AWS Lambda functions, from outside the region the function executed, will be charged at the Amazon EC2 data transfer rates as listed under "Data transfer".
16:35:01 <Shelikhoo[mds]> so there is also an data transfer cost
16:36:42 <cohosh> Shelikhoo[mds]: oh i missed that
16:36:45 <dcf1> cohosh: "getting that initial serverless URL into the hands of users is tricky" yes and that point would basically preclude its use as a rendezvous technique, because you would need some other bootstrap channel to bootstrap the serverless channel (to find out the initial URL)
16:37:11 <dcf1> so Shelikhoo[mds]'s lambda.us-west-1.amazonaws.com way seems like it would be better in that regard
16:37:21 <cohosh> dcf1: yeah, although with the use of domain fronting or using API calls instead of direct URL access, we can get around that
16:37:42 <dcf1> I'm not sure that domain fronting alone is a fix, but I have to understand it better.
16:38:31 <dcf1> If we embed a static xyz123.lambda-url.us-east-1.on.aws domain name in clients, it's like, how do we choose that domain in the first place, and what makes it not get blocked
16:38:50 <dcf1> or do we dynamically and independently generate them at each client (maybe the paper said something about that?)
16:39:33 <cohosh> on the note of domain fronting, there was an interesting observation in the paper that the chosen URL doesn't need to match a valid URL that corresponds to an existing serverless instance
16:39:57 <cohosh> and that a different front can be used per request
16:40:12 <dcf1> 6.4.1 "we followed the serverless function's URL-generation rule; a string of fixed length randomly mixed with letters and numbers for a unique URL, particularly in AWS Lambda."
16:40:18 <dcf1> is what I was thinking of
16:40:40 <dcf1> okay, so maybe domain fronting is sufficient if the client can generate its own front domains
16:41:15 <cohosh> that's pretty cool
16:42:10 <Shelikhoo[mds]> I should note here that if we generate a url that do not exist, and server response a distinct error message for that, a censor can probe if a client is generating random fronting domains
16:42:10 <dcf1> cohosh did you have another in your series of questions?
16:43:00 <dcf1> otherwise I want to quickly share my main observation
16:43:13 <cohosh> go ahead
16:43:38 <dcf1> It looks like both modes of censorless (vanilla and private) are fundamentally request–response oriented
16:44:01 <Shelikhoo[mds]> * fronting domains(never mind, aws return AccessDeniedException for unknown url)
16:44:09 <dcf1> vanilla mode operates like a normal HTTP proxy (including doing all the normal HTTP proxy stuff like knowing the difference between hop-by-hop and end-to-end headers, for example https://bitbucket.org/umass-lab/censorless/src/842c2e4458042d210c15f516fdc5fbb902a6f315/censorless-vanilla/local-proxy/src/proxy.ts#lines-42)
16:45:04 <dcf1> private mode has a separate VPS after the serverless bridge, but still (and correct me if I am wrong), what it does is gather up an entire client upstream blob of data, sends it to lambda, and then gathers up a separate downstream blob of data back to the client.
16:45:12 <dcf1> DayeonKang[mds], is my understanding right about this?
16:45:52 <dcf1> In other words, it doesn't seem like you could use censorless, even in private mode, for an interactive TCP protocol like SMTP, where there's intermixed client sending and receiving on one connection.
16:46:13 <DayeonKang[mds]> Yes, right. For the private mode, we used SOCKS proxy and made a longer connection
16:46:44 <dcf1> Okay. There is still a point on that I don't fully understand in section 7, "we employed a custom proxying protocol that persistently tracks connection state across serverless bridge invocations", but I can ask you about that in more detail separately
16:47:18 <meskio[mds]> could do some longpolling, where clients keep always a request open to be able to get data back, but that will make extra request
16:47:20 <dcf1> anyway, it seems like it would be possible to remove most of the complexity from censorless while at the same time remove most of the limitations (including the request–response limitation)
16:48:12 <dcf1> and that's basically to do the meek/champa thing, where between the client and the VPS you have a tunneled encrypted channel (e.g. TLS), and the serverless bridge in the middle just transmits chunks of the that long connection
16:48:49 <Shelikhoo[mds]> dcf1: No, lambda do not typically allow streaming response
16:48:51 <dcf1> I.e., the censorless bridge in the middle is not aware of request/response boundaries, that all happens at a different layer. the serverless bridge's only function is "I received a blob of data, forward it and let the endpoints figure it out"
16:48:59 <dcf1> Shelikhoo[mds]: no, you misunderstand
16:49:08 <dcf1> I think that's the same misunderstanding in the paper as well
16:49:17 <dcf1> You don't need streaming responses at the serverless bridge
16:49:30 <dcf1> All you need is individual request–response exchanges
16:49:54 <dcf1> the main point is that those (lambda-layer) request–response exchanged *do not have to match up with* the actual user-layer request–response exchanges
16:50:11 <Shelikhoo[mds]> okay, I understand now...
16:50:16 <dcf1> it's the turbo tunnel idea, basically: aws lambda is just a device for carrying bytes from one end to the other
16:50:22 <DayeonKang[mds]> Lambda has an option for streaming response, so we used that mode. but it was quite short streaming
16:50:32 <Shelikhoo[mds]> yes....
16:50:47 <dcf1> the endpoints interpret those bytes however they want, which includes for example reconstructing long-lived stream from them
16:51:34 <Shelikhoo[mds]> I think this is a nice idea to bundle data from different connections to a single lambda req-resp by treating the connection as a shared channel
16:51:36 <dcf1> DayeonKang[mds]: the model I think of is: there's a TCP-like "transport layer" between the client and the VPS: it notionally works on streams. The serverless bridge in the middle is more like an "IP layer": it operates on the level of "packets", not on streams.
16:52:00 <dcf1> DayeonKang[mds]: I can elaborate more on this idea in an email.
16:52:38 <DayeonKang[mds]> Sounds good! I have give you my email address
16:52:55 <dcf1> yes, that's great
16:53:15 <dcf1> anyway, this is good work, you can see it has caused a lot of interested discussion
16:53:18 <Shelikhoo[mds]> I think v2ray already support this architecture with request transport + req2udp + rrpit...
16:53:31 <cohosh> yeah this is awesome
16:53:37 <dcf1> Shelikhoo[mds]: yes, I could believe that it could be constructed with v2ray already
16:53:39 <Shelikhoo[mds]> but anyway, I think lambda is a nice option for signal channel
16:53:47 <DayeonKang[mds]> my email address is dayeonkang@umass.edu / kang.dayeon@me.com
16:55:16 <Shelikhoo[mds]> but in any case, some design does not work well in real test, even if perfectly valid otherwise
16:55:48 <Shelikhoo[mds]> so I am not sure v2ray's current building block would work well without testing
16:56:08 <Shelikhoo[mds]> anyway, anything more we wants to discuss in this meeting?
16:56:12 <dcf1> yeah. DayeonKang[mds] the signaling channel could be an interesting one to pursue, you might want to think about how an ultra-simplified version that just sends 1KB upstream and receives 1KB downstream could work, no sessions, no migration
16:57:07 <dcf1> maybe using the domain name variant that does not embed a user id
16:57:08 <cohosh> yeah i am very interested in the signalling channel, sqs has been great but is tricky to maintain and pay for, this could be a great replacement for that
16:57:12 <dcf1> ok that's all from me for now :)
16:58:12 <onyinyang[mds]> sorry was trying to get caught up until now, but very nice paper @[Dayeon Kang] :) Thanks for you work on this!
16:58:31 <DayeonKang[mds]> Thank you so much!!
16:59:03 <Shelikhoo[mds]> thanks for the nice paper and joining the meeting Dayeon Kang !
16:59:36 <meskio[mds]> yes, thanks for bringing new ideas
16:59:51 <meskio[mds]> I just added the signaling channel idea to our wiki: https://gitlab.torproject.org/tpo/anti-censorship/team/-/wikis/Signaling-Channels/channels
17:00:18 <Shelikhoo[mds]> #endmeeting