Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: cmd/go: secure releases with transparency log #25530

Open
FiloSottile opened this Issue May 23, 2018 · 41 comments

Comments

Projects
None yet
@FiloSottile
Copy link
Member

FiloSottile commented May 23, 2018

[The text of this proposal is outdated. Find the whole proposal here.]

This is a proposal for a long-term plan to provide transparency logs to verify the authenticity of Go releases. It's not something we are ready to implement anytime soon.

Transparency logs are append only Merkle trees which are easy to audit, and provide efficient proofs of inclusion. They are used for Certificate Transparency and are starting to be used for binary transparency.

They are a good fit for securing releases:

  • The log will fetch releases directly from the source, punting the spam issue on GitHub (etc.) or domain registries (as we can ban accounts/domains).
  • Clients will ask the log(s) for the release hash, and for proof that it was included in the append-only log. Module authors can audit the logs for their own projects, or get notified about new versions of it.
  • An hypothetical go release tool can trigger submission of the version to the log, and then verify that its hash matches what the developer has on disk. This is especially nice as it keeps the host (i.e. GitHub) honest.
  • Logs can also gossip with each other to make sure that a different version has not been observed before. (This is important so that two logs don't end up disagreeing on a version hash when the author changes the tag in between two log submissions.) go release can also check with logs that a version does not exist yet before tagging it.
  • Logs can be audited by third parties by comparing their entries to the packages fetched from git (maybe using the GitHub API to learn about new releases as soon as they are pushed) or by clients by comparing their global (#24117) or observed modverify files.
  • Proxies can be integrated with this system so that they will verify packages they are proxying. We can then support the concept of a trusted proxy, so that for example internal company systems will connect only to the proxy and not to the external logs.

The security of such a system is superior to what is provided by modverify, which is effectively pinning to the view of the developer adding the dependency. Transparency logs pin to the first time the version was globally observed, and with the go release workflow they pin directly to the view of the developer who created the dependency.

We can probably build the implementation on top of Trillian, a transparency log (and map) implementation which has the explicit concept of "personalities" for the custom use-case logic. (CT is a Trillian personality.)

Ideally, these logs would be operated by multiple players in the community, and a client could choose to trust or submit to any number of them.

We can build the tooling outside the go tool as a way to check/generate modverify entries to experiment until we feel comfortable with it.

@gopherbot gopherbot added this to the vgo milestone May 23, 2018

@rsc rsc modified the milestones: vgo, vgo2 Jun 6, 2018

@rsc rsc modified the milestones: vgo2, Go1.12 Jul 12, 2018

@rsc rsc changed the title x/vgo: secure releases with transparency logs cmd/go: secure releases with transparency logs Jul 12, 2018

@rsc rsc added the modules label Jul 12, 2018

@bcmills bcmills modified the milestones: Go1.12, Unplanned Nov 15, 2018

@bcmills bcmills changed the title cmd/go: secure releases with transparency logs proposal: cmd/go: secure releases with transparency logs Jan 18, 2019

@gopherbot gopherbot added the Proposal label Jan 18, 2019

@bcmills bcmills added NeedsDecision and removed Proposal labels Jan 18, 2019

@bcmills bcmills modified the milestones: Unplanned, Go1.13 Jan 18, 2019

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Feb 27, 2019

Not sure what the difference is between this issue and #24117.

@FiloSottile

This comment has been minimized.

Copy link
Member Author

FiloSottile commented Feb 27, 2019

#24117 is about a system-wide go.sum (which might be made superfluous by this).

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 4, 2019

Got it. I retitled #24117 to avoid the confusion.

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Mar 4, 2019

Change https://golang.org/cl/165018 mentions this issue: design: add 25530-notary.md

gopherbot pushed a commit to golang/proposal that referenced this issue Mar 4, 2019

design: add 25530-notary.md
See https://golang.org/design/25530-notary.

For golang/go#25530.

Change-Id: I1b4add8fe1c2f6911e925bafab99eb7418aa67b4
Reviewed-on: https://go-review.googlesource.com/c/proposal/+/165018
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 4, 2019

Published formal proposal: https://golang.org/design/25530-notary.

@rsc rsc modified the milestones: Go1.13, Proposal Mar 4, 2019

@rsc rsc changed the title proposal: cmd/go: secure releases with transparency logs proposal: cmd/go: secure releases with transparency log Mar 4, 2019

@rsc rsc removed the GoCommand label Mar 4, 2019

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 6, 2019

@arschles, you raise a good point about the configuration burden. These things seem clear to me:

  • It must be possible to opt out of verification for some paths.
  • The proxy cannot be trusted to tell developers which paths have opted out.
  • An environment variable like GONOVERIFY is needed for the general case.

I like your team1/team2 example and I think it does motivate some way to share configuration, like in your "light proposal". It may be that focusing on this one variable is wrong and that there should be a per-module go.env file (in the sense of #30411). We need to figure out what the right mechanism is, and to limit the complexity.

In the "heavy proposal", can you help me understand the scenario in which it makes sense to have a notary for private modules? If an organization can't trust its own internal TLS connections, whether to their source code server or their internal proxy, they're in a pretty bad place, right? Having an internal notary seems either unnecessary or else not nearly enough.

@marwan-at-work

This comment has been minimized.

Copy link
Contributor

marwan-at-work commented Mar 6, 2019

I agree with @rsc that private code does not necessarily need to be verified. But there's a good chance that you're in such a large company you want to make sure the code you share across the company has a shared go.sum that everyone validates against.

However, the problem with GONOVERIFY whether it's in a committed file as @arschles suggested or whether it's in an env var, is that we end up with two bad choices and can only choose one of them:

  1. Leak all import paths by default.
  2. Do not verify anything by default.

The reason for this is because the proposal suggests turning on the Notary by default in 1.13.

This will cause a hard failure if a user (or a CI/CD) did not specify GONOVERIFY and has private code the moment they upgrade to 1.13. Not only the private import paths will leak, but the failure will break the compatibility promise because once 1.13 comes out, many people will upgrade and watch most of their builds fail.

Suggestion:

Would it be possible to not turn on the notary by default in 1.13? One way to do this, is to look for GONOVERIFY and if it has values, then notary should be turned on, otherwise it's off.

And maybe in 1.14 or higher, we can turn the Notary on which would have given the entire community "enough" time to migrate.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 6, 2019

@davecheney, we're aware of the concern about various countries and looking into what we can do, if anything. Please note that https://support.google.com/a/answer/2891389?hl=en is a page specifically about Google sign-in for business services (G Suite), and to be very clear, nothing about the notary requires Google sign-in.

If someone wants to set up an alternate notary, that's easy, and we will publish a reference server (https://go-review.googlesource.com/q/f:notary). If Go users want to use an alternate notary, that's easy too - edit $GOROOT/lib/notary/notary.cfg once it exists. If someone in a country blocked off from Google sets up a notary and other users decide to change their Go setups to trust that notary, that's completely OK.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 6, 2019

@marwan-at-work, I'm trying to understand the part about Go 1.13 vs Go 1.14. If we delay changing the default until Go 1.14, doesn't that make all the bad things you mentioned about Go 1.13 happen in Go 1.14 instead? What is it exactly that you expect users to do with their "enough time" during the "Go 1.13 has been released but Go 1.14 has not" window?

@marwan-at-work

This comment has been minimized.

Copy link
Contributor

marwan-at-work commented Mar 6, 2019

@rsc I was just hoping to get a confirmation that this proposal would in fact break people's builds and leak their import paths if they were not aware of GONOVERIFY. Is that assumption correct?

Delaying to 1.14 is just one suggestion, which I think would give people more time to ensure they have a GONOVERIFY configured. There are a few other options of course, but I just wanted to double check my assumption above first before diving deeper into alternatives.

Thanks

@arschles

This comment has been minimized.

Copy link

arschles commented Mar 6, 2019

@rsc good point regarding internal TLS, I didn't think of that!

Considering @davecheney's comment about locales, my thoughts about internal organizations, and @marwan-at-work's question about essentially leaking private module names by default, would it make sense long term to make the notary opt-in, and easier to configure for other notaries? I see a few benefits to those two properties:

  • The opt-in behavior helps prevent folks from shooing themselves in the foot and leaking private module names (@marwan-at-work posed this one)
  • Making it easier for folks to configure their machine to talk to a different notary allows organizations to verify public modules while still reducing the likelihood that private module names get leaked
  • Making it easy for any developer to point their toolchain to a different notary makes verification more accessible to folks in locales that can't ping golang.org (@davecheney posed this one)

Here's a rough proposal on how a local toolchain could be configured, with ideas taken from above comments and #30411:

  • If the developer does nothing, verification is turned off
  • The Go tool still comes with the notary.golang.org public key built-in, and allows developers to add a notary=default or similar to a project-local go.env. Setting to default turns on verification using notary.golang.org and the built-in public key
  • If developers want to run their own notary, they can override it in the project-specific go.env or in their machine-global notary.cfg. notary.cfg should take precedence to protect against cases where a single private codebase is misconfigured. A few notes on running notaries that use notary.golang.org:
    • The proposal doesn't indicate that you can specify a custom notary URL in notary.cfg, so that would need to be added
    • If a private notary is going to cache tiles from notary.google.com and also verify private modules at the same time - which it will need to unless you can turn off verification per-hash in the go.sum - then it will need to re-sign the values returned by the google notary's /latest endpoint

There are some assumptions above. Looking forward to hearing what you think.

@arschles

This comment has been minimized.

Copy link

arschles commented Mar 6, 2019

Oh, and I didn't mention above that the rough proposal enables I think 4 different ways to configure verification, which feels like too much complexity. I'm of course open to reducing the options, but I do think that some kind of per-VCS-repository configuration is a must

@marwan-at-work

This comment has been minimized.

Copy link
Contributor

marwan-at-work commented Mar 7, 2019

I think it would be a huge win if we had the notary on by default. Imagine if users had to manually opt-in to HTTPS. The internet today might still vastly be in HTTP.

The only problem with turning the notary on by default, is the fact that Go will never be able to automagically find out which import paths are public and which are private.

Therefore, I'd like to throw in to the mix the idea of turning on the Notary by default while still preventing private import paths from leaking to the network.

To be able to turn it on by default and not break everyone, here are the options I can think of aside from my "delay" option above:

1. Consider the on-by-default feature a breaking change and bump to Go 2.0:

This can be done in lieu of 1.13 or maybe have the notary be opt-in for 1.13 (and higher), as @arschles suggested, until Go actually wants to introduce 2.0 and only then do we include the notary as an on-by-default feature.

2. Let the notary be turned on by default, but under some conditions:

Looking at how the community is using Go today we can make the following assumptions:

A. Most people right now are not using a GOPROXY.

B. I can safely say that many people are using Go Modules while many are still using Dep or older alternatives. That can be summarized in the fact that: they are using regular VCS fetching.

Therefore, we can potentially detect the user's current state (old or new) and based on that turn on/off the notary verification.

For example, we can turn on the notary based on the following conditions:

Condition 1: if the Go version in go.mod is 1.13 (or above), turn the notary on.
Condition 2: if the Go version in go.mod is Go 1.12 (and below) and GOPROXY is on, turn the notary on.
Condition 3 if the Go version in go.mod is Go 1.12 (and below) and GOPROXY is off, turn off the notary.

We can potentially get rid of Condition 2, and just have Condition 1 be the only way a notary is on-by-default.

gopherbot pushed a commit that referenced this issue Mar 7, 2019

cmd/go: add notary simulation and GONOVERIFY support
As an experiment to better understand the impact of
having an authoritative source of truth for module hashes
before the real notary is available, this CL adds the basic
notary authorization checks using a partial whitelist of
known go.sum values for popular modules.

In addition to the temporary whitelist, this CL adds code
implementing $GONOVERIFY, a new 'go help modules-auth',
and clearer error messages for verification mismatches.

See #25530 for notary proposal.
Filed #30601 to remove whitelist when notary lands.

Change-Id: Ibcb6ac39c5e60455edf003d8c20af6932aeb7e88
Reviewed-on: https://go-review.googlesource.com/c/go/+/165380
Reviewed-by: Bryan C. Mills <bcmills@google.com>
@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 7, 2019

would it make sense long term to make the notary opt-in

Sorry, but no, it wouldn't (as Marwan already said). One of the things we do in Go is make sure to have the right defaults out of the box. "Insecure by default" is the wrong default.

One idea I have gone back and forth about is maybe making the lookup send SHA256(module)@version instead of module@version. Obviously the notary does not know how to invert SHA256 in general, so it would only be able to answer for module paths it was already aware of. But then we'd need to have a separate way to tell the notary about a new module path. So 'go get' of a module unknown to the notary would fail after leaking only its SHA256 hash, and then you'd have a choice:

  1. If it's a private module, set GONOVERIFY appropriately.
  2. If it's a public module, run something like 'go notify module' so the notary will recognize SHA256(module) in the future.

And then you run 'go get' again and this time it works. I don't really like the complexity of that. If most corp users would be using their own proxy anyway (which could reject any notary lookups for private packages itself), then there's no benefit to this added complexity. Maybe the default would be module@version but there could be an 'opaque mode' that sends SHA256(module)@version instead. Or maybe the reverse. Or maybe something else entirely.

@FiloSottile

This comment has been minimized.

Copy link
Member Author

FiloSottile commented Mar 7, 2019

I don’t know if it’s worth the complexity of the manual submissions either, but if we do it, we should do it with k-anonymity, where we send a short hash prefix and answer with all possible matches. Module names have very little entropy, so reversing SHA256 is not actually hard.

@marwan-at-work

This comment has been minimized.

Copy link
Contributor

marwan-at-work commented Mar 7, 2019

So 'go get' of a module unknown to the notary would fail after leaking only its SHA256 hash

@rsc this would still be a breaking change as far as I can see. If so, can that be avoided by looking at go.mod's go version?

On another note, can GONOVERIFY and/or GOPROXY be included in go.mod as new directives similar to require and replace? Or potentially a new file format in the module root as Aaron suggested?

@flibustenet

This comment has been minimized.

Copy link

flibustenet commented Mar 7, 2019

About privacy concern, maybe notary and proxy to Google should be opt-in to be compliant with EU law.

@thepudds

This comment has been minimized.

Copy link

thepudds commented Mar 7, 2019

Perhaps this is off-base, but if the information that is ultimately sent is SHA256(module)@Version (or perhaps a prefix or similar for k-anonymity), then it seems like a 'go notify module' would be a one-time operation over the lifetime of a given module path for a public module?

If so, I wonder if 'go release' could have a role here? Setting aside with the exact flag might be, something like 'go release -public' could do normal 'go release' behavior plus perform the operation suggested for 'go notify module' above. That might help shift the expectation and behavior to be something that a public module author expects to do (once) if they want to facilitate consumers of their public modules.

In addition, or perhaps alternatively, I wonder if it would be feasible for 'go release' when run without any '-public' flag to check at that moment in time whether or not the module is known to the notary, and report that back to the user. Depending on the ultimate resolution of GONOVERIFY behavior, perhaps 'go release' could check that as well. That could perhaps help public and private authors do the right thing.

Under that proposal, 'go notify module' or similar could still exist for the hopefully rarer case where a public module author forgot to do the right thing but a module consumer would like to get the information into the notary.

In short, 'go release' could be seen here as helping codify some best practices of things to do when releasing a module.

@thepudds

This comment has been minimized.

Copy link

thepudds commented Mar 7, 2019

Also, would the index service be able to automatically trigger getting the large majority of SHA256(module) in place for public modules?

From https://blog.golang.org/modules2019:

And the index service makes it easy for mirrors, godoc.org, and any other similar sites to keep up with all the great new code being added to the Go ecosystem every day.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 7, 2019

@marwan-at-work, I don't know what you mean by "breaking change" in this context. Tools like the go command are not covered by the Go 1 compatibility document. They change behavior from time to time.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 7, 2019

@FiloSottile, k-anonymity seems like overkill. Also it doesn't work: if we send SHA256(path)@version and the notary knows what path must be but doesn't have that version yet, it can go grab that version. If we send a truncated SHA256 then the notary has to go (try to) grab k versions, which is k-inefficient.

Remember, the problem I am solving with the SHA256 is just "don't tell the notary a private import path". I am not solving "don't tell the notary which module you are interested in at all". The latter problem is solved by proxies, which can prefetch the entire database and serve it themselves.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 7, 2019

@thepudds, yes, once there is a 'go release' we definitely want it to validate that the copy the notary sees matches the local copy, and that would imply telling the notary about the path. And yes, 'go notify module' is only once per path so the vast majority of users would never need to run it - someone almost certainly already has.

@arschles

This comment has been minimized.

Copy link

arschles commented Mar 7, 2019

Fair enough regarding turning verification on by default

I think me and @marwan-at-work (correct me if I'm wrong, Marwan) are both trying to prevent build breakages out of the box (i.e. turning off noverify for private modules), but approaching it from slightly different ways. And I've been talking a lot about making it easier in the UX to use other notaries (i.e. for folks who can't access golang.org or who want to use other notaries).

In the former, I agree with @marwan-at-work's conditions, and ideas to set noverify modules in the go.mod. To help with the latter, the only thing I'd add to the options in the go.mod is making it easier to set or point to the public key of the notary you want to talk to. Also in the latter case, I don't think sending anything up to notary.golang.org is an option in the cases we need to be looking at for the latter, so I'm not sure if that solves anything.

@arschles

This comment has been minimized.

Copy link

arschles commented Mar 7, 2019

I also have concerns over adding a go mod notify because essentially that requires that someone who releases a new version module path needs to add something to their CI/CD systems (or do it manually) to run go mod notify, right? That would be a big diversion from the current workflow for all module authors, which is tag it and you're done. That would disrupt another extremely common workflow that affects lots of people (even if it isn't covered under the compatibility guarantee)

edit: s/version/path

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 8, 2019

@arschles, I'm pretty reluctant to add configuration variables to either go.mod or go.sum. Your notary/proxy configuration is a local decision, not one that should be exposed to all clients of your module. Cloning someone's repo and cd'ing into it probably shouldn't default you into a whole separate proxy/notary/etc. And 'git checkout <commit from one year ago>' most probably should not roll you back to last year's servers either. (Imagine how badly git bisect would fail!)

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 8, 2019

@arschles, I share your concern about the extra step in go notify. That's why it's not in the proposal. But if using the SHA256 hash makes people sufficiently more comfortable with the notary, it might (or might not) be the right tradeoff. That said, I don't see why a CI/CD system would need to run go mod notify, nor would most users.

First, my assumption about CI/CD usage is that you only push to that system once you've at least built the code locally. If you've built the code locally, the go command updated go.mod and go.sum to include any newly-added dependencies. Any entries in go.sum are always accepted as correct. A complete go.sum therefore implies no notary access at all.

Second, we added the -mod=readonly flag exactly for CI/CD systems, so that even if go.mod were not up to date they would not try to update it. I don't know whether -mod=readonly also applies to go.sum today, but it should, and if not we'll fix that (#30667). If a CI/CD system is using -mod=readonly, then, an incomplete go.sum will trigger a local failure, again no notary access at all.

It's true that during local development, some developer somewhere in the ecosystem will have to run go mod notify once for each public module path. If we roll it into go release, the author can do it easily. If not, the first user will. Most users will never need to do this. In fact, if you try to use a public module and find that you need to run go mod notify, that's a very strong signal that literally no one else has ever used that module as a dependency, and you might rethink blazing that trail. :-)

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 8, 2019

More generally, any answer we reach for the default behavior is going to be some compromise between:

  • defending against server and network compromises (do nothing),
  • bandwidth (download the entire notary database and look for what you need),
  • and privacy (be more selective in what is downloaded).

It's not going to be possible to satisfy everyone, with any decision. Ultimately the goal of this discussion is to try to find a default behavior that is as acceptable as possible for as many people as possible. I really appreciate everyone engaging respectfully and helpfully as we work through this.

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 8, 2019

I've updated the design doc, expanding the Security and Privacy sections quite a bit to capture the discussions to this point.

@thepudds

This comment has been minimized.

Copy link

thepudds commented Mar 8, 2019

FYI, I think this is the recent diff on the proposal document and the corresponding CL, if interested.

I agree putting something like an actual proxy server into a go.mod would not be desirable, and I am not in love with putting a large amount of additional configuration into go.mod, but I wonder if some form of user intent regarding public vs. nonpublic might make sense in go.mod so that it could be checked in to VCS, which could then drive sanity checking of the other related configuration settings or environment variables by things like go get, go release, etc.? I don't know that it would ultimately make sense to do that, but if that was to be pursued, would it be just two cases that really matter regarding user intent (e.g., some type of public vs. nonpublic in go.mod)? Of course, there would be follow-on thinking needed about defaults, dealing with Go 1.11/1.12 go.mod files, how/when it is set or checked, etc., but perhaps it might be reasonable to have effectively one bit encoded somehow in go.mod regarding intent.

Separately, regarding sending the hash(modulepath)@version, it seems like a nice win, and while it might be a modest increase in complexity in one area, it might be a net reduction in overall complexity (e.g., if it ends up driving down complexity in some of the related questions around other settings, or by reducing the complexity or penalty of having a "wrong" default for a subset of users). The biggest downside seems to be the potential for complexity around getting the hash into the notary. It would not be great if the end result was "you need to read the documentation to understand when, how, and why to invoke go notify <module>... but it certainly at least "feels" like people could be successfully guided by the go tool almost 100% of the time via some combination of default behavior, informative messages suggesting likely resolution, automatic validation, flags, etc. as part of the otherwise natural module workflow (e.g., for go get, go release, or perhaps even go init), in addition to the indexing service doing it automatically for the large majority of repos on major public code hosting sites.

@arschles

This comment has been minimized.

Copy link

arschles commented Mar 13, 2019

@rsc the issues that config variables in the go.mod sound like a good reason to leave them out, I agree.

Since GH issues don't have threading, I've submitted two posts in golang-dev to capture two of the earlier questions I've asked in here. I'll focus this just on the SHA256 / go mod notify idea. I've tried to gather some related points made in previous posts and I'll try to address them here. I apologize in advance if I missed something important. I certainly don't intend to cherry pick points or build strawmen here.

My assumption about CI/CD usage is that you only push to that system once you've at least built the code locally.

+1, this is mine too. As you said, that would mean that go mod notify was run somewhere for all of your dependencies, but it would still be up to a developer to run go mod notify on their own if they're releasing a new module path of their own. That's the part that concerns me because it would be the first time that module authors would not be able to git tag and be "done" with releasing their new module path.

Even though that's definitely not a good practice, it happens a lot when folks are prototyping something new or don't have any tests at all. I'm guilty of the latter, on multiple counts 😁

That's a very strong signal that literally no one else has ever used that module as a dependency, and you might rethink blazing that trail. :-)

I'd say so too 😄. However, this means that either the author has to run go mod notify (see above) or most new packages don't get used. In fact I'd guess that the only new modules that get adopted would be from "trusted" (or "famous") authors

I've updated the design doc, expanding the Security and Privacy sections quite a bit to capture the discussions to this point.

Thank you 😄

I wonder if some form of user intent regarding public vs. nonpublic might make sense in go.mod

@thepudds can you help me understand how the intent would be acted upon by the go tool? I'm trying to get a sense of how the sanity checks differ from skipping verification

@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Mar 14, 2019

Just as a heads up, I am going to be away from work (including GitHub!) starting pretty soon and continuing for the next two weeks, ramping back up gradually the week of April 1. I'll catch up on any discussion here and on golang-dev when I return. Thanks for the excellent conversation so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.