Hacker News new | past | comments | ask | show | jobs | submit login
JSON Web Tokens (jwt.io)
467 points by adamnemecek on Sept 8, 2014 | hide | past | favorite | 74 comments

I think writing your own variation of this at one point is a sort of rite of passage for web developers. I thought I was so clever with my custom PHP framework and sessionless backend. That said, it's done because it works well and makes sense. I'm glad to see we're finally settling on a recommended way of doing this.

I've recently incorporated JWTs into my own application. One of the questions I'd like to ask other HNers is how you deal with token expiration. I don't want to reissue a token if I don't have to, but I don't want to create permanent tokens either. An obvious solution is to embed an expiry in the token and check against that, however I don't think that'll lead to a very good UX if tokens "randomly" expire. I've come up with two solutions: 1.) Each user has a session seed token that is used as the secret and thus a user can log out of all sessions by requesting a secret change. This works because any previous JWTs are now invalid because they cannot be validated with the new secret. 2.) I could ping the server periodically (or decode the JWT on the client side) and check when the token expires. Shortly before the token expires, I could request a new one. This doesn't solve the problem of tokens expiring when the user is offline though. Anyone have any better solutions?

Yes, it is a bad UX that the session expire randomly and how to handle this is a whole subject per se.

Here are my two cents:

- Web Apps: If you set an expiration of 1 week, do not use the token for 1 week. Use it for a day and then use the token to get a new token. This way every time your user uses the application, he gets one extra week logged in. This is not different than the normal concept of session and cookies.

If the user does not use your application for a week, next time he go to your app, he will have to login again and this is fine and widely accepted.

- For Mobile/Native Apps: you can use the same explained above, but that's not how most of the mobile applications work these days, I can open facebook after a month without using it and I'm sure I will not need to login again. One way you can do this is to use a refresh token that never expires to fetch a JWT that does expires. The problem with a token that never expires is that it never expires, what happen if your phone is stolen?. In my opinion, there must be a clear and easy interface where the user can revoke these tokens but looking at random alphanumeric characters wont help, so the best practice will be to show something like "Revoke access on John's IPad" this means that the refresh token must be requested for an specific device name.

Certain events can trigger things, if you lost your wallet (at least in Argentina), first thing you do is to call the bank to disable your credit cards. If you forget your laptop in a friend's house and you don't want then to read your Facebook you will probably change your password. Changing the password is usually a good event to

1. Revoke all refresh tokens (for native apps)

2. Anchor the "iss" date for JWTs, this means that tokens issued before the last password change are not longer valid.

Because changing the password can have other purposes, you can also put a prominent button in your UI: "Close session on all browsers and devices", this will delete all refresh token and store the timestamp to check against the JWTs.

We might write a blog post soon about this on Auth0's blog: https://auth0.com/blog.

The OAuth way of doing things is to have two tokens: an auth token and a refresh token. The auth token is the one you send with your requests to authenticate them (and can be a JWT), but it has a very short expiry time (say an hour). The refresh token is typically a random string that's stored in your database and can be used in place of a username/password or other credentials to get an auth token.

This way you can store (in an app, or a browser) the less sensitive refresh token (which might expire after a week or so) instead of the highly sensitive permanent auth credentials.

I've been thinking about the same problem lately and have come up with an approach for my use-case that might work:

* Set expiration to a low value (~15 minutes)

* Every generated JWT also gets added to an "issuedTokens" collection for each user

During JWT validation, if the expiration has passed, an "Expired" response would be returned from the server (ex. 401 w/ "Expired" in body). When the client receives this status, it should initiate a refresh process which trades an expired token for a new one.

The refresh endpoint on the server should take an expired token and perform the following:

1. Validate token (except expiration)

2. Retrieve user ID and check if token is in its issuedTokens collection

3. Issue a new JWT

4. Remove the expired token from the collection and add the new one

Upon failure of any of these steps, an Unauthorized error should be sent to client which then requires logging in again.

To prevent a never-ending build-up of issued tokens, we can set a TTL on the tokens in the issuedTokens collection. Set the TTL value to the amount of time that a login should be active for before requiring logging in again.

This approach doesn't hit the database unless you keep trying to refresh an expired token. In which case you can make use of a cached blacklist of failed tokens. This can reside next to the application itself if treated as a cache layer.

This is definitely just a work-in-progress solution that I'm about to test out. Let me know your thoughts on it.

One way of doing it is having an out-of-bands way of refreshing tokens in responses. So if the token is about to expire you can return an updated one on the side-channel.

We've been doing this for a while in our angular apps using response interceptors: http://engineering.talis.com/articles/elegant-api-auth-angul...

This is a great write-up.

For those (like me) wondering just how exactly you use those tokens, I found this quick introduction: http://www.intridea.com/blog/2013/11/7/json-web-token-the-us...

Looks definitely easier than SAML.

>Looks definitely easier than SAML.

With the exception that it's only signed, not encrypted, yes. SAML does have the advantage in that regard, but I've never been a fan of using clients to transport sensitive information anyway, even encrypted.

JWTs can be encrypted instead of signed (see the JSON Web Encryption standard: http://tools.ietf.org/html/draft-ietf-jose-json-web-encrypti...).

Thanks for spreading the word Adam!

We (Auth0) are heavy promoters of the usage of JWT. Here are some articles about using JWT instead of cookies on single page apps with APIs, and its pros/cons.



JWTs seem really great to handle auth on single page apps with APIs, but I don't quite understand something yet:

What is the best and recommended way to implement a "remember me" feature?

I'm using the "angular-fullstack" yeoman generator (https://github.com/DaftMonk/generator-angular-fullstack) which uses your npm packages, but it lacks of a feature like that, and the app is pretty much unusable, user must log in every time after closing the browser...

Store the token on a cookie or localstorage (see store.js for instance).

This is a pretty elegant solution for delegated authority, too. If you have client-side code as well as server-side code that both access an API, JWTs can give you a uniform way of handling authorization at the API level.

Client side access is pretty well understood. A JWT can be stored in a cookie and then added to Ajax requests as a bearer token, for example.

The server-side code will typically have some way of validating the client, such as an encoded cookie. This will be used to build a user object of some sort, which can be used to run authorization checks. When the server side needs to access the API, the user object can be serialized into a JWT and added as a request header, similar to typical client-side access. If it's done well, the API can use a very similar user object to check authorization. Another option is just to do JWT pass-through. If this is done, the signature should at least be checked to avoid facilitating an impersonation attack.

JWTs have built in signing to ensure that the user object is intact. All that is left is to encrypt the channel with SSL.

The pattern I've seen used most commonly is the latter situation you described. The user authenticates, and is issued a signed JWT which includes their user ID and (optionally - I actually talk about this in my comment here: https://news.ycombinator.com/item?id=8283530) an expiration. You can also delegate roles and permissions through the JWT, which makes for a really slick authorization pattern if you've got a case where you'd like to easily change a user's permissions. I mean, you can add whatever you want to a JWT - I guess I'm just offering suggestions based on my own experience. IMO, user ID is the best way to go, because it's only a single field you have to maintain. If you start adding too much information in there you might end up with a bunch of tokens with old property names etc. Personally I just tried to closely follow what OAuth specified should be added to OAuth JWTs. I think I've got "issuer", "audience", "expiry" and "id" in my current tokens, but adding a list of permissions would be a good choice too in my opinion.

I think what everyone needs to remember is that SSL is a must for these things. If you're issuing non-expiring tokens over non-SSL you're just giving the keys to the castle away. Another thing that should be considered is token storage. I'm using local storage right now which works for me, but it just means I need to be more careful than ever about XSS exploits, because there's no "HTTP only" option for local storage!

Shameless plug, but just gave a JWT talk at DjangoCon this week.


Is there a video of your talk available?

I can't answer your question specifically, but confreaks.com has an event page for DjangoCon, so hopefully it will pop up on there.

Yea they recorded all the talks, guess it'll be a couple of weeks before they pop up.

I'm surprised this has picked up a fancy name and full libraries for so many languages. Not to mention the use of "JWT" as the acronym, which is already famous for "Java Web Toolkit". It's just an HMAC token for validating the integrity of the data - it proves the data has not been modified, that's it. People have been using this for years, perhaps most popularly used by Facebook to sign API requests and responses.

A side note for anyone considering the use of timestamps within the payload for expiry: note that if you are going to have multiple machines verifying expiries, they will need to be using NTP to ensure that all machines have the same current timestamp at all times.

'just an hmac token', but attached to the payload in a well-structured way. Very required for the proliferation of libraries across all languages.

There is also JSON Web Encryption, built on top of JWT, which provides encryption in addition to signatures. https://tools.ietf.org/html/draft-ietf-jose-json-web-encrypt...

JWTs are a pretty great solution for various authentication and authorization related problems, like OAuth tokens, password reset tokens and the like. Using an information-bearing (and signed) token like a JWT tends to make your web apps less stateful and simpler, by offloading the problems of determining authorizations to a single, central location.

The main downside is that the representation (base64-encoded json) is pretty bulky for something that might be used in HTTP headers and URLs, but it does make them easy to debug.

Yes, this is a real problem, tokens can get very large if you put too much information.

HTTP does not define any limit for headers, but most of the Web Servers have a default limit, Apache 8kb, IIS 16kb, nginx 4kb (see this question on SO [1]). If your JWT exceeds these limits you will get a "413 Entity Too Large".

It gets only worse if you need to use a query string instead of a header because the limits are lower and IE has a limit too [2].

Solution: carefully design your JWT, what information you will put on there. You usually don't need everything, store only the things you will use on every request, like the user id, name, email, roles are good candidates. Do not try to put the entire Facebook or Linkedin profile on a JWT.

Beside the limits, it will make the request bigger to transport and maybe even slower to verify.

[1]: http://stackoverflow.com/questions/686217/maximum-on-http-he... [2]: http://stackoverflow.com/questions/417142/what-is-the-maximu...

This seems like a case where Base-85 [1] would have been a better choice than Base64.

[1] http://rfc.zeromq.org/spec:32

Why would you consider Z85 to be a better choice? When you take URI encoding into account, Base64 is both simpler (no need for %-encoding)[0] and more space efficient (1.75 URI characters per byte for Z85, and 1.33 for Base64)[1]. The very first line on jwt.io says, JSON Web Token (JWT) is a compact URL-safe means of...; being URI-safe appears to be an explicit design decision.

[0] All but six of Z85's special characters get encoded by Javascript's encodeURIComponent:

    : + = ^ / ? & < > [ ] { } @ % $ #
[1] URI-safe versions of Base64 (as used by JWT) use 4 characters for every 3 bytes. Z85 uses 5 characters for every four bytes, but 17 of those 85 characters require three URI characters to represent, meaning that a Z85-encoded byte requires 1.75 URI characters per byte on average:

    (68 * 1  + 17 * 3) / 85 = 1.4 uri_chars/char
    1.4 * 5 / 4 = 1.75 uri_chars/byte

Thanks, that's a nicely explained correction.

The HTTP standard actually allows 90 distinct octets in cookie values, but I suppose you're not going to quibble over <2% coding efficiency given Z85s other advantages.


For the Python users out there, the wonderful itsdangerous (http://pythonhosted.org/itsdangerous/) library offers the ability to generate JSON Web Signature compliant tokens.

I have used itsdangerous a lot with DRF and it works really well, good call.

Just a heads-up: the font-weight of your lead paragraph makes it too light on Chrome+Windows, some of the diagonals pretty much disappear.

Same with Firefox on Linux. The 'S' letters are very noticeable.

Thanks for reporting this! I just open an issue here:


How secure are JSON Web Tokens? I understand the basics of JWT, but I'm not an encryption expert.

- What is the most secure algorithm to use when creating the signature?

- Should you do anything else on the server side besides verifying the signature? IE: tracking tokens, rotating keys, etc.

Without knowing much about encryption it seems risky to simply trust a JWT based on signature alone. How hard would it be to actually spoof a JWT with the most secure encryption algorithm?

The concepts of JWT encoding and encryption are orthogonal. While they can be used in conjunction with encryption, the question of whether or not the tokens themselves are "secure" is outside of the scope of their use. All standard encryption practices apply and there isn't anything special about using encryption with JWTs.

Answering your questions completely depends upon your application. There is, in general, no "most secure" algorithm for encryption and what you handle on the server completely depends upon the security goals of your application.

For example, I'm currently working on something that passes JWTs unencrypted and unsigned because the application simply doesn't need it. Furthermore, the application does have a method of tracing used tokens, but there isn't really a need for rotating keys or anything else like that.

Hope that helps.

Thanks that does help.

I get the fact that security totally depends on implementation. I just need to better understand encryption and the differences between the various algorithms. If anyone knows of any good resources for learning about cryptography and encryption algorithms that would help a lot.

That's probably best addressed by the “security considerations” section of the JWT spec:


My short answer to “How secure are JSON Web Tokens?”, though, would be that the relevant security issues are likely to come in “around” the use of JWTs, not as a result of JWTs themselves being “weak”. JWT is just a set of conventions for encoding authn/authz claims in JSON and then encrypting/signing the result.

tldr about jwt

1. They're made up of three .-delimited segments: header.data.sig

2. JWT doesn't prescribe any particular sig algorithm. The header indicates by what method the {data} JSON object was signed to get {sig}. A common sig method is hmac-sha

3. JWT only provides a signature to assure that the {data} was not tampered with. The data is not private at all and easily inspected (which is nice for debugging). JWE can provide encryption http://tools.ietf.org/html/draft-ietf-jose-json-web-encrypti...

And for some: "Why is this useful?". It's useful if my API needs to receive requests to do serious business like delete things or post as your account. How do I know if the request is from a trusted source? Well if the trusted source has a secret key they can use to generate the {sig} according to the {data}, then I can verify that the requester had access to the secret key and therefore trusted.

How does this not lead to a situation where you are trusting the client for authentication/authorization information?

Tokens can be either signed (using JWS [0]) or encrypted (JWE [1]), which means that you're just trusting that your encryption/signing key hasn't been leaked.

[0] https://tools.ietf.org/html/draft-ietf-jose-json-web-signatu...

[1] http://tools.ietf.org/html/draft-ietf-jose-json-web-encrypti...

The client must send authentication credentials to the server. For example, a username/password that's provided by a user. There's nothing initially stored on the client that allows it to authenticate without user input.

Of course, you could use a holder of token scheme to authenticate in the case of server to server communication. However, in this case you're making an assumption that both servers can prevent access to the shared secret.

You got that right.

In addition to what you described, the JWT spec also supports an RS family of algorithms (asymmetric). It means that you can sign with a private key and verify with a public key.

For instance Google, sign every JWT with the same private secret and publish their key for verification [1]. If you use this, your server-side application not only needs to verify that the JWT signature is correct but also that the audience is your application.

I wrote an example middleware for connect to verify Google's JWTs [2].

[1]: https://developers.google.com/accounts/docs/OAuth2Login#vali...

[2]: https://www.npmjs.org/package/connect-google-jwt

It looks to me like the data is cryptographically signed

The JWT is signed. You can optionally encrypt the data in the token.

However, the data in the main request is not encrypted, unless you're doing so via another means.

Is the advantage of using this instead of basic auth and HTTPS that you don't have to use basic auth and HTTPS? (Or if not basic auth, an authenticated session cookie over HTTPS.) I've seen many people write their own system to avoid implementing HTTPS. Which, as much as installing certs is expensive and complicated (especially keeping track of expiration) should probably be done for all web sites for other privacy and security reasons.

Or is this for use in URL-passed data (like email links?) If so, is this better than including a token as a key to a one-time, limited-time activity stored on the server?

MAC schemes like JWT provide message integrity and a form of authentication that can't always be provided by TLS/HTTPS.

The simplest way of describing it is that with HTTPS, when sending data to/from a server and a client (the client may even be another web server) you can be assured no one but the server or client can see or tamper with the data in transit.

But the client is still able to trick the server, or vice versa, at the application level. To prevent that you need a message signing scheme (aka a MAC), and this is an example of one.

For the case of password reset links from an email, generally speaking there's no point in using something like this; a one-time random token that you track and expire on the backend makes more sense. You'd only want to use a MAC if you need to freeze some kind of state in the URL and retain it, like perhaps a user's IP address. Then you can verify that the user's real IP address does indeed match the IP address in the URL; they can't tamper with the URL to change the IP address if a MAC is used.

I don't know why anyone would want such a feature for a password reset function, and you could also replicate that same behavior just by storing the IP on the backend as well (though while also perhaps making your application somewhat more complex in the process), but that would be one example of doing something like that in a secure manner while still letting the client keep track of the state instead of the server.

One common use of tokens like this is to defer state retention to all your clients so your server can do less work per request and store less data. Simply verifying that the MAC for a message is valid in every request is a lot faster than making a database/data store query to return session info for every request.

A minor-ish clarification on terms: a MAC algorithm (e.g., hmac-sha1) should not be conflated with a digital signing algorithm (e.g., rsa-sha1), as the MAC requires knowing the secret in order to validate. MACs are fine for checking that data haven't been tampered with, but do not have the properties of an actual signature that you can verify without being able to reproduce.

Yep, absolutely. I said "a form of authentication" because it allows some degree of message authentication, but not to the same degree a proper digital signing mechanism that utilizes public key cryptography would. The term "signing" is also kind of overloaded, and doesn't necessarily mean signing in the sense of a human signature. HMAC-SHA1 and RSA-SHA1 (or PGP) both can be said to "sign" a message, but they mean different things.

Generally for web app development, I think MACs tend to suffice for the vast majority of use cases.

JWT only signs the token itself not the body of the request, so message integrity is still an issue without TLS/HTTPS

The advantage is particularly that you don't have to use basic auth. Basic auth in a browser context is more or less a non-starter. The browser's basic auth implementation will take over and provide a generally poor, uncontrollable user experience.

For server-to-server authentication, there is less to recommend this, although it does allow services to authenticate without having to check a credential against a data store someplace.

Which is sad, otherwise things like SRP could have become built in as well.

I guess you could could kludge it into a form with a method="SRP" and have the browser do it's thing with the username and password field.

If you have a payload that does not require encryption, but does require authentication, then Hawk (from WalMart) is an option.



Supported langs: https://github.com/hueniverse/hawk/issues?q=is%3Aclosed+labe...

You should still use HTTPS with this. The signing used here is not to protect the payload, but to ensure that an authority that you trust has issued the token.

JWTs are used to great effect in Google's Wallet Digital Goods apis to allow you to control which payments are allowed and authenticate postbacks ( https://developers.google.com/wallet/digital/docs/tutorial ). It's a shame that service seems to be more or less dead...

I hope it doesn't die! I'm building a service based on it :)

I think it'll be around for a while but it's definitely in a strange place. Some of the pages like the signup page and checkout settings page ( https://checkout.google.com/inapp/merchant ) just give a service unavailable error half the time, and it's been that way for months. And in the merchant center, the 'Business insights' section doesn't work and hasn't for months either. Last week we couldn't access our order history for a few days. The sandbox payments environment doesn't work most of the time either, and even production sometimes has errors loading the payments screen.

It's also really limited in that there is no API to get order status to check if a subscription is still active - so if you miss the cancellation postback, you have no way of knowing which subscriptions are active and which aren't. You also can't change the value of an active subscription. That was the biggest thing people were asking about when they introduced it at IO three years ago, and it's still not fixed.

So if you are in a country where you have a better option you might want to consider it :)

Kudos to Auth0 for creating this site. The education, awareness and graphical debugger are great. Thank you!

We (Firebase) use JWTs to integrate with an existing app's userbase[1].

[1] https://www.firebase.com/docs/web/guide/simple-login/custom....

JWT is a fairly elegant choice in cases where you have a session-less API backend that is consumed by an SPA and/or mobile clients.

I've been really pleased with how simple and straightforward it is for implementors. It's good to see it getting more visibility. Thanks to Auth0 for educating the community.

For an angular js implementation with server examples:


It's still a very new project, but looks promising.

Alternatively + [IMHO] more robust security-wise (although both great projects): https://github.com/lynndylanhurley/ng-token-auth

Ah, that looks much more mature than Satellizer.

The only qualm I would have is that there's only a Ruby server side example, with Node as the test suite. I would say expanding that to include more example languages would be beneficial.

What's the Auth0 Java JWT library like?

I've been using the Google library[1], in a dropwizard app and it's pretty old and not great.

OTOH, I do recommend both Dropwizard (the OAuth stuff lets JWT drop straight in) and Satellizer[2] for Angular.JS JWT stuff.

[1] https://code.google.com/p/jsontoken/

[2] https://github.com/sahat/satellizer

Forgive my ignorance (I know I'm missing something), but how do you distribute the shared secret to clients of a single page app? How do you keep the shared secret "secure"?

I've implemented something very similar to JWT (based on the old AWS auth scheme), but each client is given an API Key and their own Shared Secret. How does the server know whose shared secret it should use to validate messages?

I think I can answer my own question.

The client doesn't get a shared secret. JWT is a server sending a token to the client which the client then sends back like a cookie. The client isn't allowed to modify the token or the data inside it.

It's like a signed cookie.

We've been using JWTs for our single-sign-on integrations (and some other tokens) with Livefyre customers for a couple years now. No downsides at all! Customers that aren't familiar with JWT are easily able to create tokens with all the libraries available in nearly every language. It's been depended on in some other lovely protocols like OpenID Connect. Great standard.

Sorry if this is a dumb question, but what's exactly the difference between JWT and OAuth?

(EDIT: typo)

To start with, OAuth is an authoriation protocol and JWT is a token format (a signed and/or encrypted piece of data). So they are orthogonal.

The OAuth protocol doesn't specify which token format to use. Normally people have been using an "opaque" token, this is a token that doesn't have any meaning nor content but the meaning is stored somewhere. Here is an example of a table storing tokens and the scopes (permissions) associated to them. Every time an API Call is made, you would have to check against this table to see if the token sent in the Authorization header has the necessary scopes to call the API.

token | scopes oiajoeihfe9jh9283n | read.userinfo, post.friends uhuernvmiwmwo38h2g | read.userinfo

If you would use JWT you wouldn't have to store that information because it would be part of the token (i.e. stateless)

This would be a counterpart JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3O DkwLCJhdWQiOiJodHRwOi8vbXlhcGkiLCJzY29wZXMiOlsicmVhZC51c2VyaW5 mbyIsInBvc3QuZnJpZW5kcyJdfQ.tnLKyCWhVfkj2v15maCBJBVgPO08zFp2Lh n4Vkb4OoU

header: { "alg": "HS256", "typ": "JWT" }

payload: { "user_id": 1234567890, "aud": "http://myapi", "scopes": ["read.userinfo", "post.friends"] }

Google now changed their Authentication protocol to use OpenID Connect [1], which is a layer on top of OAuth plus a JSON Web Token. The thing about OpenID Connect which makes it useful for Authentication is that you can verify that the token (JWT) has been issued for your application (and not be used by another application) which was something the OAuth was lacking and a common vulnerability [2].

[1] https://developers.google.com/accounts/docs/OAuth2Login [2] http://homakov.blogspot.com.ar/2012/08/oauth2-one-accesstoke...

JWT contain a payload, thus the token itself can contain auth information, while OAuth tokens are "dumb" and you have to store the token somewhere serverside to verify authentication. This makes JWT great for simple APIs.

Does anyone think JWT should replace cookies for session management in non-single page apps? I'm guessing you'd have to include an AJAX call to determine if you're logged in on each page, which seems kind of odd to me.

I made a scala lib for jwt - needs some work still but has eg extractor functionality. https://github.com/jasongoodwin/authentikat-jwt

We've been using them for a zendesk SSO integration, it was pretty painless.

How do you invalidate tokens?

You can store the expiry time in the token itself. Then it is up to your server to validate that the token is still live. If you need to mass invalidate every token, you change the signing key.

So is there any way to do single-sign-out if using these tokens for authentication?

just for the record there are some serious critics of oauth http://hueniverse.com/2012/07/26/oauth-2-0-and-the-road-to-h...

I find jwt a tinge too complex. On the other hand no solution for unified AAA exists on web that makes sense: HTTP is a stateless protocol.

I sometimes wish people wake up from the Oauth insanity and realize using a statefull TCP connection would drop the sessions problem.

Why use stateful protocol (Oauth) for a stateless protocol (HTTP) over a statefull protocol (TCP)?

Emitting token lasting more than 1 hour impersonating the user in an hostile environment seems quite a lot of risk (mathematically speaking)

I must be a grumpy old dev, but I kind of hear the oauth sweet song of easy secure cross security token like sirens trying to seduce me for crashing my ship on the havoc of security.

Security is a mine field. One step out of the «right way» and boom, it does not work while burning 25% of your CPU power, an hidden tax.

Oauth protocol is a multi-dimensionnal maze designed under LSD at my opinion, and I kind of feel it unsafe to take.

Applications are open for YC Winter 2022

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact