Extensions next steps

Standardize Profile Extensions Now; Deal with Comprehensive Extensions Later
Extensions can be usefully divided into two basic types—those that fit under the definition of a profile and those that make more drastic changes, which I'm calling "comprehensive extensions".[1] I think we should release the system for handling profile extensions ASAP, and then figure out the comprehensive extension mechanisms later.
Releasing a mechanism for comprehensive extensions needs to be done with some care,[2] but officially supporting profile extensions is relatively easy and it would immediately "unblock" the community, since there are a ton of issues that could be addressed by profile extensions. Allowing the community to develop extensions for these features, and to start using them right away in their apps in a compliant fashion, would be a huge win, and it doesn't preclude us from standardizing any of the things they develop if those ideas pan out and when we have more bandwidth.
Finally, we've already kept people waiting far too long for some official extension system, which is my fault, so let's just get something out the door!
(Note: focusing on profile extensions first doesn't block the operations extension, which we could release in a backwards-compliant way even if the comprehensive extension mechanism isn't ready.[3])
Profile Extension Design Goals
Negotiation for and discovery of profile extensions is dead simple, because it's largely unnecessary.[4] Instead, I think the interesting design problems are around:

preventing an extension from conflicting with another extension, or with a future base spec addition, used in the same request/response document (optionally, while allowing data provided from multiple extensions to be interspersed with each other);

surfacing recommended extensions and discouraging use of ones that have known issues (since setting good conventions is a huge part of the spec's goal);

figuring out if/how a published profile extension can be changed/added to over time;

making it easy for someone to quickly make an extension and, if need be, get it registered/approved, so that people trying to innovate/experiment don't feel like they're banging they're head against unneeded restrictions;

making sure that, if we have some kind of review process (e.g. to control quality), we aren't blocking peoples' projects during that time.

An open question is whether we want to incorporate successful extensions into the base spec over time and, if so, how we can do this more-or-less seamlessly after the extension has been widely adopted with the extension syntax. Tentatively, I'm leaning towards "no, an extension should always remain an extension (and we should just make the extension syntax nice)". Under this approach, we could require extension registration and then just have a very prominent list of "recommended extensions" on the site. This should give us almost the same convention-setting power as formally incorporating an extension into the base spec, while giving us much more flexibility (we can replace extensions on that recommended list over time, if necessary) and saving us the design headache of creating a syntax that supports a transition from usage as an extension to usage as a standardized feature (which is harder than you'd think; see here).
Design Proposal
To satisfy these goals, I've been considering the following design:
Syntax/Definition
A profile extension would be applied to a JSON API document by adding a new key–value pair at the top-level of: the document, a resource object, a relationship object, a link, a resource identifier object, or more than one of these places. (Profile extension data could not be added within "attributes" or directly under "links" or "meta".) Every place that a given profile extension is applied, it would have the same key (but, of course, the value could change). Also, each applied extension would be listed in a top-level profile link (details below). So, for example, a hypothetical "last-modified" profile extension might be applied as follows:
{
  "links": {
    "profile": [/* details later in this proposal */]
  },
  "last-modified": "2015-10-01",
  "data": [{
    "type": "articles",
    "id": "2",
    "attributes": { /* */ },
    "relationships": {
       "tags": {
         "links": { /* */ },
         "last-modified": "2015-09-01"
       }
    },
    "last-modified": {"date":  "2015-10-01", "changed-fields": ["title", "tags"] }
  }]
}

This is just to show that the extension could add data in multiple places; that it would always add that data under the same key name; but that the type/meaning of data would be different in different places (e.g. "changed-field" may only make sense in a resource object) and that these semantics would be defined entirely by the profile extension.
Registration Requirement
All profile extensions would need to be registered, but registration would be granted very quickly. The criteria for approval wouldn't involve whether the proposed design is good, but just that the registration request:

includes a suggested a key name at which the extension's data should be placed. (For the example above, this would be "last-modified".) This key name should be the same as the key name used by other registered extensions that do very similar things, or the same thing with a different syntax (if any) and different from any registered extensions that this extension expects it could be used with.

includes a specification, detailed enough to allow for interoperable implementations, of the meanings/types of values that can occur as this extension's data, at the different places where that data can occur. Like the base specification, each extension's specification should include must-ignore rules to make it extensible over time, but this isn't a requirement.

includes some basic contact info for the registrant (name and email, plus perhaps some optional and/or private fields like website and phone number).

defines an extension that fits the definition of a profile extension (i.e. doesn't try to override the meaning of base spec members or define new data in invalid places)

reuses objects/patterns from the base json api spec where appropriate. For example, if the extension defines a field that points to another resource, that field should hold a resource identifier object. Or, if an extension uses the key "type" it should only use that key as the base spec does (i.e. to hold a string indicating a resource's type). Additionally, if the extension includes a mechanism for doing something "architectural" that other extensions may need to do too (e.g. if it defines a syntax to point to another member of the document), and we don't have a standardized mechanism for doing such a thing, the extension may be held a bit for special consideration to check that its proposed mechanism, were it to become a convention, wouldn't have any majorly bad architectural ramifications.

(Optional) includes the minimum base spec version that the extension requires, if not 1.0.

(Optional) includes a URI by which to identify this extension, that should dereference to the extension's specification document. If no URI is provided, the extension's specification would be hosted permanently on jsonapi.org, and the extension's identifier would be a jsonapi.org URI.

After a registration request is received, there would be some very brief window (no more than a week) for one of the spec editors to check that it satisfies the three conditions above. This period might also allow for public comments, which could be useful just in case someone catches a major problem with the submission that the author didn't know about (e.g. that an extension already exists that fits their needs). But again, the point of required registration isn't to do a design review—it's to prevent certain conflicts (see below) and support extension discovery—so the author wouldn't be bound to act on these public comments.
Before submitting a registration request, authors would be encouraged to see if an existing extension could be extended to fit their needs. And they would be advised, if so, to ask the author of the existing extension to consider tweaking it to cover their case. The existing extension's author may reject the request, of course, but should respond to it within a reasonable timeframe (2 weeks). If the author of the existing extension had died, moved out of contact, or otherwise couldn't make changes that were important to the community, the JSON API editors could reassign responsibility for the extension, to allow it to continue to evolve.
(For truly experimental features whose behavior is too unstable to be standardized, the "meta" key would continue to be an immediately-usable, api-specific playground.)
Using Profile Extensions

The profile extensions in use on a given document would be listed in the Content-Type header (as the profile parameter of the JSON API media type) and in the document's top-level "links" (see below).

Clients could request particular profile extensions by listing all those they support in the profile parameter of the JSON API media type that they include in their request's Accept header, as described in #650.

The "profile" key in the document's top-level "links" would hold the URIs identifying any profile extensions used in the document. If a profile link is given in "link object" form (i.e. not just a string), that link object would have an extra allowed key, called "key". The value of "key" is the name of the key throughout the document at which the extension will put its data. If "key" is absent, its default value is the extension's suggested key (from its registration). So, consider the following:
"profile": ["http://jsonapi.org/ext/last-modified", "http://jsonapi.org/ext/actions"]

Here, the suggested key for the "http://jsonapi.org/ext/last-modified" extension is "last-modified" and the suggested key for the"http://jsonapi.org/ext/actions" extension is "actions". Therefore, the above is equivalent to the more explicit:
"profile": [
  {"href": "http://jsonapi.org/ext/last-modified", "key": "last-modified"},
  {"href": "http://jsonapi.org/ext/actions", "key": "actions"}
]

Now, suppose a server wants to send a document with both the http://jsonapi.org/ext/actions extension and the http://jsonapi.org/ext/transitions extension, and both were registered with the same suggested key, "actions". To apply both to the document, the server must change one or both of their "key" values, so that they're distinct. Whenever a sender manually sets an extension's key, the key's value must begin with an "_" followed by the extension's suggested key. So the server's profile links might look like so:

"profile": [
  "http://jsonapi.org/ext/actions",
  {"href": "http://jsonapi.org/ext/transitions", "key": "_actions2"}
]

The payload would then have two keys, "actions" and "_actions2", holding the extensions' respective data. Usually, though, extension negotiation and the registration requirements would combine such that two extensions that have the same suggested key would not both be needed in the same document. So this "key": "_{name}" provision is more of a fallback/escape hatch for special cases.
Future (Base) Spec Updates

Because each extension would only be able to appear under its suggested key or an underscore-prefixed version of the same, the base spec editors could freely add any keys that no extensions have registered with (and that don't start with an underscore), without risk of conflict. After such a key was added to the base spec, future extensions would not be allowed to register with it as their suggested key.

A profile extension's specification could also be updated, by submitting an updated registration request with the new definition, to accept an expanded set of values, as long as those new values wouldn't break existing clients.

Rationale
I didn't preface my proposal above with any rationale because I want to know if, unexplained, it strikes people as way to complicated. If that's how you felt, let me know! Or, even better, I'd love suggestions for how to simplify the design, while ensuring it still meets the goals listed earlier.
If you're curious about how I got to this particular strategy for avoiding extension conflicts, the various iterations I went through are here (listed from top to bottom).
Footnotes

See this comment and the rest of that thread for background on the utility of this distinction and the definition of a profile extension.

Figuring out the constraints and negotiation rules for comprehensive extensions is much harder than for profile extensions, since comprehensive extensions are less likely to compose and be future proof by default. Hence considerations/approaches like this, which would now also need to consider this.

We'd do this by making operations an optional part of the base spec. This would be totally backwards compatible because: clients that send "operations" data would be opting into the extension by virtue of forming that request, and servers would signal their support by returning a 2xx, whereas as current/non-supporting servers would return a 4xx on account of the missing "data". Likewise, if clients want to retrieve operations data (e.g. a change list), they can simply append a new query parameter (e.g. ?operations) to their request to opt-in and, again, a server understanding the request would send a 2xx while one that doesn't (including all existing servers) would send a 4xx—a consequence of our rule about rejecting unknown query parameters. Moreover, such a design for operations would not be inconsistent, as it's already how the optional sorting and include requests work.

Either party (the client or the server) can send extended data and, if the recipient doesn't know how to interpret some or all of the extensions, it'll just ignore the ones it doesn't understand. Beautiful! If a server wants to minimize unnecessary data transfer, it can choose to send data for a profile extension only if the client indicates that extension in its Accept header; if a client wants to check to do the same, it can perform an OPTIONS request first (as defined in #650) and only send data for supported extensions. Again, really simple.

@ethanresnick Thanks a lot for your proposal. I'm currently working on a client/server implementation and I realized that implementing extensions (especially comprehensive extensions) isn't that easy (there are no extensions of course, but I implemented it in a way, that allows an extension to change anything, e.g. allow an array, where the base spec defines a string).
I think your proposal is a good start, but I have a lot of things to note:

What if an extension needs to add more than one new "key"? E.g. your last modified example could also include an identifier of the user who modified the information. All your suggestions are based on one-key extensions, although a "profile" isn't limited to adding only one information.

You wrote, that the same key should be re-used by multiple extensions. I think this can work, but I also see some potential conflicts with this approach. E.g. your last modified example: one extension can require a different time format, another extension expects the local time, not the UTC time, a third extension expects a date object (as generated by .NET). And what if another extension expects to save the last modifing user with this key? So if you want to re-use keys that are added by extensions, the keys must be named properly for future use by all other extensions, and the value should comply with a globally defined format for specific data types. For yor example I'd say that "last-modified" should not be allowed, but named like "last-modified-datetime". This would be clear for all and also solve the problem with other information regarding the last modification, e.g. the user identifier which could be saved in "last-modified-user-id".

Later you wrote, that in case two extensions using the same key "actions", the client/server should replace this key automatically. I don't understand this. Why are they replaced here, and why to you re-use the key in the previous example at the same time? How can the client or server know that? IMHO this is confusing and unnecessarily complicates the whole format and processing on both sides a lot.

If I'm right the suggested replacement of keys (also with additional numbers added) can result in keys, that already resulted from other replacements. That's theoretical, but who knows which or how many extensions and keys are used in the future? The solution should ensure, that no conflicts can occur.

Extension cannot save any data in "meta", because the keys can freely defined there. So I don't think that can be used by any extension, stable or not.

I don't think that all extensions can be checked and approved that quickly. E.g. see the discussion about resource versioning, with many good but very different points of view. What if an extension gets approved, although it only covers half of the problem? What if the extension developer doesn't want to change it, because it's enough for his needs? Should I write my own extension and compete with it? Or should the community be able influence the extension development?

I'm missing a solution for extension versioning. If a client lists a profile in the request, the server could respond with a newer version of the extension, than supported by the client. Also the server could have problems to parse the request without this information. Also extensions could require a specific version of the base spec, so perhaps not all extensions/extension versions can be used with each version of the base spec (e.g. an extension that adds a key to an object that is added with JSON API 1.1).

Generally I see the problem, that extensions aren't as well discussed as the JSON API spec itself, which could result in problems with later versions. Especially when breaking previous versions is not allowed, this can result in many "dead" extensions, and new extensions doing very similar things...

Suggestion: Better name spacing
I still suggest to use defined prefixes for extension attributes. This would allow a clear seperation of extension keys and avoid all the possible conflicts and confusions mentioned before.
Here an example, based on your concept ("key" replaced with "namespace", which can be set to any value, as long as it complies with the member naming rules and is unique for the document - seperated with a special character here):
  "profile": [
    {"href": "http://jsonapi.org/ext/last-modified", "namespace": "l"}
  ],
  "l.last-modified-datetime": "2015-12-11T19:00:00.000Z",
  "l.last-modified-user-id": "8cd89ffa-6de6-45fb-a26f-83c4d5572ddf"

Or with a seperator similar to your suggestion (also possible, because starting with an underscore, but requires more space and is IMHO less readable):
  "profile": [
    {"href": "http://jsonapi.org/ext/last-modified", "namespace": "l"}
  ],
  "_l_last-modified-datetime": "2015-12-11T19:00:00.000Z",
  "_l_last-modified-user-id": "8cd89ffa-6de6-45fb-a26f-83c4d5572ddf"

Advantages:

Allows multiple keys to be added by extensions.
The resulting document is better readable, because keys are not replaced by a prefixed and perhaps multiple times suffixed value.
Real name spacing, no "some keys are replaced, some not"-behavior.
No conflicts with the different value expectations (with the costs of perhaps duplicate data in different keys, but that's less problematic I think (gzip)).
It's in most cases more compact (no key listing and a very short, automatically set namespace prefix).
You can develop profile extensions without unnecessary limitations, like saving data in "meta" - but now extensions really CAN securely add data to such members with high conflict risk.
No key limitation in future versions of the base spec (in your suggestion, only "keys that no extensions have registered" are possible).
No conflicts means, that extensions can be developed without your review. So only extensions that want to be "official" and listed on your site need the review - and in this case the review can be more strict and take more time. If I write an extension for a special web service, with a very special purpose, clients can use it for this web service - it doesn't have to be reviewed or listed on your site, but it's still ensured that it works with the base spec and all other extensions, reviewed or not.

@ziege thank you for engaging so thoughtfully with this!
Extension Approval

I don't think that all extensions can be checked and approved that quickly... What if an extension gets approved, although it only covers half of the problem? What if the extension developer doesn't want to change it, because it's enough for his needs? Should I write my own extension and compete with it? Or should the community be able influence the extension development? ... Generally I see the problem, that extensions aren't as well discussed as the JSON API spec itself, which could result in... many "dead" extensions, and new extensions doing very similar things...

I totally agree that existing extensions that are incomplete should be expanded when possible to cover a new use case, rather than someone registering a competing extension. I've added some text about how that could work as the second to last paragraph of the registration section in my original post.
But I don't expect that that will be a cure-all. There will be some extensions that are so poorly designed that they can't be expanded well. And there'll be other cases where there really are substantially different approaches competing to solve the same problem. And I think that's ok.
In fact, this natural competition was part of the impetus for my suggestion to have related extensions all register with the same key. That way, we can list all the extensions with the same key in the same section of the registry, and users who need the feature in question can look through those options and pick one. Over time a clear winner should emerge (perhaps we'd track some data on the registry's site to see this happen), and then we can officially recommend that. Conversely, some extensions would become "dead", and we could show those much less prominently.

Extension cannot save any data in "meta", because the keys can freely defined there. So I don't think that can be used by any extension, stable or not.

Right; ignore what I said about "meta". I was trying to figure out what it's role should be in all this, given that it already exists, but the truth is that it should probably be used for nothing once we have a proper extension system. Its only valid use would be to hold data that an API knows won't be standardized (which is the type of data I was trying to get at by talking about formats that are changing frequently), but the truth is that it's more-or-less impossible for an API to be sure that something it needs won't also be needed by other APIs and become a convention.
Extension Versioning

If a client lists a profile in the request, the server could respond with a newer version of the extension, than supported by the client.

But such a newer version would be backwards compatible (i.e. only adding some possible new values, all of which the client can ignore if it doesn't understand them), so this shouldn't be a problem.

Also the server could have problems to parse the request without this information.

A server is allowed, under the base spec, to require that the client provide certain data (e.g. it can return a 4xx if authentication credentials aren't provided). But I'm not sure what this has to do with extension versioning. If an extension is expanded to allow more information to be provided, all the information the server needs will still be there.

Also extensions could require a specific version of the base spec, so perhaps not all extensions/extension versions can be used with each version of the base spec (e.g. an extension that adds a key to an object that is added with JSON API 1.1).

If an extension only applies to some, e.g., JSON API 1.1 and the recipient only supports 1.0, then the recipient will just ignore the new 1.1 object and any data the extension added inside of it...which is how profile extensions are supposed to work.
So, long story short: I don't think there's any need for an explicit versioning scheme.
Namespaces vs Implicitly Qualified Names
It seems like the main discussion point is the relationship between traditional namespacing, with explicit prefixes, and the strategy I proposed above for managing conflicts, which I'll call "implicitly qualified names". I did consider an approach like the one you're suggesting, so I knows its advantages. Some of those advantages have come to seem more important than I first realized, so I've extended my proposal below to incorporate them. But, for other (possibly subjective) reasons, I prefer the extended version my proposal below to a full switch to namespace prefixes. Let me take you through my thought process...
First, it's key to know that my proposal above does actually guarantee that no conflicts will occur, just like traditional namespacing. Under that proposal, whenever any extension is "imported" into a document (by being listed in the "profile" link), it automatically has a "key"; its key is either the one specified manually in the link object or the "suggested key" that it was registered with. So, when I had these two links earlier:
"profile": [
 "http://jsonapi.org/ext/actions",
 {"href": "http://jsonapi.org/ext/transitions", "key": "_actions2"}
]

That was just a shortcut for:
"profile": [
  {"href": "http://jsonapi.org/ext/actions", "key": "actions"},
  {"href": "http://jsonapi.org/ext/transitions", "key": "_actions2"}
]

Now, the "key" of every extension used in the same document has to be unique, and its the job of the document's sender to ensure this. In that sense, "key" is exactly like "namespace". There's nothing magical about the "2" in the second links "key"; it could have just as easily been "_actions-aiesntroien"—the sender can use whatever suffix it wants, as long as it makes all the "keys" unique throughout the document.
So, the end result is that there are two key spaces: 1) the "suggested keys" that live in the registry and share a space with the base spec keys, and appear in the document unprefixed; and 2) the "_"-prefixed keys, which can't conflict with each other (because the sender ensures that) and can't conflict with the base spec/suggested keys (because those don't start with underscores).
(When the sender wants to send multiple extensions that have the same suggested key, it really must create the extra "key"s with a leading underscore, since otherwise any extension could show up under any key, and the base spec could never safely add anything. But my proposed requirement that the registered "suggested key" come after the underscore isn't strictly needed for preventing conflicts; I just like it for readability/consistency.)
Given that this idea of a "default key" wasn't obvious from my initial writeup, I think it'd probably be a good idea to say that "key" is mandatory (just as "namespace" would be), so that its more explicit which values are being defined in which extensions.
The question still remains, though, why use the system I'm proposing instead of conventional namespaces?
You're right that, under my original proposal, each extension can only have one key. But that, on its own, probably wouldn't be a problem given this simple workaround. The bigger issue, I've come to think, is that my proposal makes it impossible to intersperse keys from different extensions—a benefit of namespaces that you alluded to. By this I mean that my proposal makes it impossible to support usage like the below, in which the valid:title-case key being pulled from one extension and embedded into another:
"templates:create": {
  "fields": [{
    "name": "title"
    "type": "string",
    "validate": {
      "maxlength": "50",
      "valid:title-case": true
    }
  }]
}

I'd considered this kind of usage before, but wasn't sure that we should allow it, since it seemed like there would be some risk to doing so. It definitely complicates implementation a bit, as it means an extension's keys can appear anywhere, rather than at a single, predictably-named key. And it also felt like it might encourage extensions that are too small in scope (in the name of complete composability + loose coupling), which could ultimately run counter to our goal of getting all the important extension-provided functionality widely implemented.
But you've convinced me that, at least in some controlled way, functionality like this is worth having. So I want to extend my original proposal to handle this case: now, when an extension is registered, the specification should include not only the set of values that can be used directly as the value of the extension's main key, but also a list of keys, and allowed values for each, that can be mixed into other extensions, as in the example above. So an example registration for Extension X might look like this:
Extension X does x, y, z.

Suggested key: "x".

Allowed values at main key: An object with optional keys "a", "b", "c", or a string

Keys for use in other extensions' data, and their allowed values:
  - "f": accepts an integer that parameterizes "f".

Then, with this registration, a document that uses extensions x and y might look like this:
{
  "links": {
    "profile": [
      {"href": "http://jsonapi.org/ext/x", "key": "x"},
      {"href": "http://jsonapi.org/ext/y", "key": "y"}
    ]
  },
  "y": {
    "blah": true,
    "x:f": 4
  },
  "data": {
     "x": { "a": true, "c": "blahblah"},
     // ...
  }
}

Above, the f property from extension x was mixed into the body of extension y, using the x's "key", which is already set to be unique, as the prefix.
If you compare the extended proposal above to prefixed namespacing, the key difference is this:

When a prefix is always required, the base spec can add any values it wants in the future, not just keys whose names haven't yet been registered.

But, when some members can appear unprefixed (like the raw "x"), values provided from extensions look more like base spec members visually.

So the tradeoff ultimately comes down to: do we want more freedom for the base spec to pick its names, or do we want members from extensions to look more like first-class citizens. In the past, I would've said that ultimate flexibility for the base spec was more important, but I've recently done a 180º on this, after reading RFC 6648 and Guy Steele's talk on Growing a Language. The relevant ideas from those two documents are:

If a feature gains popularity as an extension, and we then standardize that feature in the base spec, the first clients and servers that try to implement the base spec version will face strong incentives to also implement the feature as an extension, since doing so is easy and any older parties they interact with won't support the base spec version, but are likely to support the extension version. Therefore, the feature will end up being supported/reimplemented as an extension long after standardization (read: essentially forever), which makes it unlikely that standardization, which has other costs,[2] is worth it in the first place. Accordingly, we may have a lot of extensions being used long term that are de-facto standards and so really ought to look like like first class members. Having prefixes for these members strikes me a bit unseamly. (Meanwhile, the only time the "implicitly qualified" version requires prefixes, with the "_", is precisely when a de-facto standard for a given feature hasn't arisen, as evidenced by the document author wanting to include more than one implementation at a time of basically the same feature.)

Names are cheap. That is, if someone claims one name, it's not hard to be a bit creative and come up with a synonymous one that's roughly as good. I wasn't quite willing to take this argument to it's full conclusion, which would've meant a single global namespace and every extension having a distinct key. But, I accept it enough that, after accounting for multiple extensions registering with the same "suggested key", I am pretty confident that there will be enough good names left unclaimed for the base spec to choose from.

And that's basically why I prefer the implicitly-qualified version: it lets us avoid prefixes at what seems to me a cost worth paying. It also has some other random benefits, like requiring much less special-casing to support JSON-LD, with it's fixed `"@xxx" style keys.
Thoughts?
Misc

the value [of each key] should comply with a globally defined format for specific data types

By this do you mean that we should say, for example: "if your extension's going to define a field holding a date, it should use an ISO8601 string" or "if your extension's going to define a field that points to another resource, it should use a resource identifier object"? If so, then I totally agree, and I've updated my original proposal to add this as a registration requirement.
Footnotes

An extension getting only one key isn't normally an issue because the extension can just place an object (whose contents it would have full control over) at that single key, and fill that object with all the needed information. So, to take your example, if the last modified extension also needs to show the user who made the change, it would store a value like this:

"last-modified": {"date": "2015-10-01", "change-author": {"type": "people", "id": "1"} }

Moreover, if we do want to support the rare extension that would need more than one key (e.g., an extension for JSON-LD, which would presumably want @type, @id, @graph, etc), we could simply change "key" from holding a string to holding a object that maps registered key names to how they appear in the document. So extension x could register with two keys, "a" and "b". Other extensions could also register with "a" and "b" (just like multiple extensions can register with "actions"), and "key" would now default to a mapping like {"a": "a", "b": "b"} that could be overridden with {"a": "_aXXX", "b": "_bXXX"} to avoid conflicts.

Maybe the biggest cost of formally incorporating extensions into the base spec is the loss of flexibility I mentioned in the original post. But there are others too. E.g., for document senders that automatically include extension data to save the cost of extension negotiation and to get better cacheability, having an extension version and a base spec version would just force duplication in document payloads.

@ethanresnick I have a lot of thoughts and am currently working on the details to include your suggestions, convince you of some of my suggestions :), and solve some other problems I see... Will be finished within the next days.

@ethanresnick I have just finished my document. It got huge, but I had to check several use cases against the suggested solutions. I also defined some missing parts in the communication between client and server, as well as how extensions integrate into other JSON API features.
Before I post it here I want to be sure, that my results are objective and that I considered all important points in my check. My focus is the stability/extendability of the JSON API standard and a secure API update process (not to break client compatibility).
How would you define a good extension solution?
Which conditions should be met?

@ziege I'm looking forward to reading what you came up with! Thanks again for thinking about this so deeply.
As far as conditions that a successful solution must meet, I'd say that it should support all the extensions referenced in #794 that can be represented as profile extensions, and that it should satisfy the design goals from my original post (or have an approach to each of those issues). I'm also happy to add other criteria if you think those are missing anything!

Extensions for JSON API 1.1
This document tries to define the requirements to integrate profile extensions into JSON API. This includes the definition of extension rules, the definition of required changes in the base spec, and solutions for related topics like  the general version handling and several uses cases regarding the implementation of extensions.
Versioning
First of all, it has to be defined how versioning works, as JSON API 1.0 doesn't define this in detail. This includes the versioning of the base spec and extension specs.
Versioning scheme
We need a defined versioning scheme. I suggest a scheme based on the Semantic Versioning scheme with the following rules:

The version MUST be a string including a MAJOR version and a MINOR version. All changes SHOULD be backwards compatible and increment the minor version. If this is not possible (which is not intended, but we should be able to handle that) and a change leads to incompatibility, the major version MUST be incremented and the minor version reset to 0.
Optionally an additional PATCH version can be added to the base spec or extension specs, which in this case means, that the spec has been modified to clarify the intended behaviour of the minor version. So the spec itself and the intended behavior must not change. The patch version is for documentation only and to be able to track changes of the spec - implementations should not be affected by these changes, so clients and servers can ignore the patch version (except they didn't implemented the spec as intended).
Public extension versions start with version 1.0, which is the default if no other version number is given.
Development extension versions use the major version zero (0.x), starting with 0.1. Development versions must always be included in documents (to not default to 1.0).
This means, that a given version in document must be handled as follows:

The major version used by the API must be explicitly supported by the client.
The minor version set by the API is the minimum version the client has to support, so if the client supports newer compatible minor versions within the given major versions, that's fine.
Exception: In development versions anything may change at any time and should not be considered stable. Only minor versions can be incremented there and each new minor version can contain incompatibilities. So clients must explicitly supported the given development version.

Allowed changes in new versions
There is one simple rule: Each document or request following the old spec must still be valid in the new spec and handled like in the old spec. A document or request following the new spec doesn't have to be valid in the old spec.
So a new version can also re-define the allowed values for a key, but must still support all previously allowed values. For example instead of an string the new spec can allow an array of strings. Or instead of the values "x" and "y", also "z" can be allowed in a new spec.
Processing newer versions
JSON API 1.0 does not define how to process documents that contain unknown keys or invalid values for known keys. This proposal to implement profile extension regards this problem and ensures, that documents based on newer base spec versions are only used, if the server supports them, so this won't cause problems with existing implementations.
Nevertheless this case should be defined from JSON API 1.1:

When parsing the document, unknown keys MUST be ignored.
When parsing the document, invalid values for known keys (following the given specs the parser uses) have to be handled as an error. If such an error occurs on the server side, the server MUST respond with a 400 Bad Request status code and details about the error. The client SHOULD NOT repeat the request without modifications. If the error occurs on client side, it's up to the client, how to handle this error.

Profile extensions
A profile extension is a simple and therefore limited form of extension. Basically it only adds information and doesn't change anything in the base spec. So if done right, it should never be problem to use multiple extensions in the same document, no matter which information the extension adds to the document.
It's possible that extensions are standardized and integrated into future versions of the base spec.
Rules
Basic rules
The following list summarizes the most important rules for profile extensions:

Profile extensions are allowed to add new keys to existing objects of the base spec and define additional rules for the added keys and their values, but they MUST NOT re-define existing keys or values of the base spec, nor the rules defined for them. Some objects of the base spec are excluded and not allowed to be extended:

the jsonapi and profile members (introduced below)
the attributes and meta member (reserved for custom data by the API/client)
the relationships member (but the relationship objects can be extended).
the included member (but the included objects can be extended).

All added keys MUST comply with the member format defined in the base spec.
Profile extensions are not allowed to extend other extensions. If the functionality of an extension needs to be extended, a new version of this extensions with additional functionality has to be created (or a new extensions has to be created to replace the old one - but this SHOULD be avoided).
(Note: Theoretically this is possible, but it would introduces dependencies between extensions and make this topic too complex... Simplicity is good here!)
Profile extensions MUST NOT define additional request parameters.
Profile extensions MUST NOT define fixed endpoints.
Profile extensions MUST NOT re-define the header values defined by the base spec (e.g. "Content-Type" and "Accept"), but adding new headers is allowed.
Profile extensions MUST NOT require a special extension processing order by the client or server, e.g. always first or last, before/after another extension, or before processing the keys/after document creation. *(Note: This would also introduce dependency handling to no run into possible conflicts between extensions...)""
TODO: Should they be allowed to re-define the status code of a server response for specific situations?

Dependencies
Extensions can require a specific base spec version (e.g. if it requires a specific object, that is available from a specific base spec version). The requirement has to be defined in the extension spec and considered when implementing it on server/client side. This required version should not be changed later, or the extensions must be handled as not backwards compatible (see versioning scheme).
Compatibility
To avoid conflicts between the base spec and multiple extensions, all keys added by extensions are automatically prefixed (like in the example below - for details, see namespaces).
Extension developers should assume that the extension with all its key names might become standardized later. So it SHOULD be designed to cover all relevant use cases and its keys should be named properly. Also every new version of an extension SHOULD be backwards compatible (for details, see versioning scheme).
Extensions SHOULD avoid to use key names (within own objects) that are already used by the base spec, except the values of these keys are taken from the base spec (no matter if prefixed/namespaced or not). This increases the chance that an extension can be integrated into the base spec without noteworthy changes later.
"b:badextension": {
  "b:errors": [
      "A first error message...",
      "A second error message..."
  ],
  "b:href": {"b:url": "http://example.com/target", "b:lang": "en-us"}
},
"g:goodextension": {
  "g:errors": [
      {"g:title": "A first error message..."},
      {"g:title": "A second error message..."}
  ],
  "g:href": "http://example.com/target"
}

Using extensions
The extensions that can or must be use are defined by the API on server side. Extensions are optional by default, but can be marked as required. As long as an extension is optional, clients can decide if to use it or not.
Exchanging extension data between client and server
At the moment, we only have JSON API 1.0 and everything is clearly defined on server and client side, but with the ability to use different base spec versions and extensions, the document format gets flexible and there is a need to exchange additional information.
And with each new base spec version and new functionality implemented from previous extensions, as well as multiple extension versions, the server has to inform the client about the supported base spec versions and the extensions required for each version. This enables the server to upgrade to new base spec versions without breaking clients.
Version 1.0 of the base spec already introduced a way to communicate information about the supported JSON API version, but this cannot be used for all client requests, and it also doesn't solve the problem of multiple supported versions, extensions that depend on these versions and so on...
This has to be solved by modifying the Accept header for the request, because the client has to inform the server about the preferred base spec and the supported extensions. Of course information could also be included into the document itself, but we don't send a body with GET requests, and therefore this information would be missing. Although JSON API 1.0 requires to use the media type without additional parameters, this can be done backwards compatible.
Requesting API options
To get the relevant information, the client has to do request to the API base directory using the OPTIONS method. For this type of request the client MUST add the version media type parameter and list all supported JSON API major versions (if multiple major versions will ever exist) with their maximum supported minor version.
OPTIONS /api HTTP/1.1
...
Accept: application/vnd.api+json; version=1.2, application/vnd.api+json; version=2.0

The OPTIONS request allows to add a body to the response. This is used by JSON API to return a JSON document, containing information about the API options (described in the next section). For compatibility with version JSON API 1.0 servers, the client should fall back to JSON API 1.0 on empty responses (and possible 405 Method Not Allowed status codes).
This enables the server to respond with details about different JSON API versions and adopt new base specs early. It's also possible to track the Accept header information to analyze the API usage and get information about clients still using old API versions. So the API provider can decide when an old API version can be suspended.
The OPTIONS request and response can be extended with additional headers (e.g. for CORS support).
Note: OPTIONS requests are not cachable, but the client SHOULD extract the relevant information and save it for the current session to use it for the following API communication.
Server options response
The server selects a supported base spec from the clients Accept header for the response.
The response is an array, containing one ore more (base spec version dependent) option objects, which can be extended in future versions. The following example demonstrates how this could look like with multiple versions and extensions (the profile objects are described below).
Note: The format of the response must be reviewed carefully, because this is a special case and every future version of the base spec has to be understood by clients that support JSON API 1.1. Keys can be added, but existing keys MUST NOT be re-defined (e.g. allow an array where currently only a string is allowed). That's why I tried to include different use cases regarding profile extensions, their versioning and replacement - so it's perhaps looks a bit complicated first, but this is to demonstrate some edge cases.
TODO: Which Content-Type to use here?
Content-Type: application/vnd.api+json

[
  {
    "jsonapi": {
      "version": "1.1",
      "meta": {
        "description": "The block should not be send, because version 1.2 set by the client in the Accept header."
      }
    },
    "profiles": [
      {"href": "http://www.example.com/last-modified", "namespace":"lm"},
      {"href": "http://www.example.com/revision", "namespace":"rv", "version": "1.5", "status": "required"},
      [
        {"href": "http://www.example.com/auth-credentials", "namespace":"cr"},
        {"href": "http://www.example.com/auth-certificate", "namespace":"ce"}
      ]
    ]
  },
  {
    "jsonapi": {
      "version": "1.2",
      "meta": {
        "description": "Version 1.2 already includes the last-modified and revisions functionality."
      }
    },
    "profiles": [
      {"href": "http://www.example.com/revision", "namespace":"rv", "version": "1.5", "status": "required"},
      [
        {"href": "http://www.example.com/auth-credentials", "namespace":"cr"},
        {"href": "http://www.example.com/auth-certificate", "namespace":"ce"}
      ]
    ]
  },
  {
    "jsonapi": {
      "version": "2.0",
      "meta": {
        "description": "Version 2.0 already includes all the extension functionality."
      }
    },
    "profiles": [
    ]
  }
]

Client request
The client can now decide which version to use (preferably the newest one, because it's generally more compact and uses a standardized functionality instead of extensions).
Request containing profile extension information
The client decided to use the example base spec version 1.2. Therefore this version as well as the selected profile extensions to use (if any), MUST be send with the request, by setting this information for the Accept header. Other supported media types MUST NOT be set, because for this request the client expects a response in the given format only:
GET /resources/1 HTTP/1.1
Accept: application/vnd.api+json; version=1.2; profile="rv:1.6,cr:1.2"

The profile string is build from the namespaces defined by the server, and a compatible version supported by the client (namespace + ":" + version, separated by comma). The client SHOULD always communicate the newest compatible profile extension version, to enable the server to analyse the API usage (and switch to newer versions early).
When a request document is send with the request, the version and profile (if any) information MUST be added to the document:
POST /resources/1 HTTP/1.1
Accept: application/vnd.api+json; version=1.2; profile="rv:1.6,cr:1.2"
Content-Type: application/vnd.api+json

"jsonapi": {
  "version": "1.2",
  "profile": [
    {"href": "http://www.example.com/revision", "namespace":"rv", "version": "1.6"},
    {"href": "http://www.example.com/auth-credentials", "namespace":"cr", "version": "1.2"}
  ]
}

If JSON API 1.0 is the target version, the media type parameters MUST be omitted. This is required for backwards compatibility (between new clients with old servers):
POST /resources/1 HTTP/1.1
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json

"jsonapi": {
  "version": "1.0"
}

Note: Future versions MAY add additional media type parameters.
Request without profile extensions
A request based on the example base spec version 2.0 could look like this (no profile extensions required here):
POST /resources/1 HTTP/1.1
Accept: application/vnd.api+json; version=2.0
Content-Type: application/vnd.api+json

"jsonapi": {
  "version": "2.0"
}

Server response
The client has to validate if the version in the jsonapi object of the request document is supported. If the jsonapi object or the version is missing, the version defaults to 1.0 (because it's optional in JSON API 1.0). If the base spec version is not supported by the server, the server MUST respond with a 415 Unsupported Media Type status code and SHOULD provide additional error information to help the client fix this problem.
Next the server has to check if the included combination of profile extensions (if any) fulfills the server conditions. If not (extension missing, incompatible extension version, wrong combination), the server MUST respond with a 415 Unsupported Media Type status code and SHOULD provide additional error information to help the client fix this problem.
When processed successfully, the server must respond with a document using the requested version and profile extensions. The used version and profile information MUST be added to the response document - this information MUST match the defined values in the API options response (1.0 versions can be omitted):
POST /resources/1 HTTP/1.1
Content-Type: application/vnd.api+json

"jsonapi": {
  "version": "1.2",
  "profile": [
    {"href": "http://www.example.com/revision", "namespace":"rv", "version": "1.5"},
    {"href": "http://www.example.com/auth-credentials", "namespace":"cr", "version": "1.0"}
  ]
}

Profile definition
Profile extensions are added to the document by adding profile objects to a profile array in the jsonapi object.
Profile and Namespace
The profile of an extension (e.g. http://jsonapi.org/ext/signature defined in href) is its global identifier.
To prefix all keys added by an extension, the server MUST define a namespace for each extension, which is be used as an API extension identifier (e.g. in the Accept header). The client MUST NOT re-name the namespaces defined by the server in the API options for its requests.
Both, the profile and namespace MUST be unique for the document and not empty. The namespace value also has to comply with the member name rules.
"jsonapi": {
  "version": "1.1",
  "profile": [
    {"href": "http://jsonapi.org/ext/revision", "namespace":"rev"},
    {"href": "http://jsonapi.org/ext/secondextension", "namespace":"2nd"}
  ]
}

All extension keys in created documents MUST be prefixed (no "namespace defaulting") by the defined namespace, followed by a colon (U+003A COLON, ":"), and the key name, originally defined by the extension.
Optional and required extensions
All extensions are optional by default, but the server can require that the client support a given extension, e.g. if the server only accepts signed requests and therefore a "revision" extension has to be supported.
To signal this, a status MUST be added (which is also possible for optional extensions):
"jsonapi": {
  "version": "1.1",
  "profile": [
    {"href": "http://jsonapi.org/ext/revision", "namespace":"rev", "status":"required"},
    {"href": "http://jsonapi.org/ext/secondextension", "namespace":"2nd", "status":"optional"}
  ]
}

Extension selection
In some cases the server requires, that at least on of a given list of extensions must be supported by the client, e.g. if the server supports different extension for authentication (let's say "credentials" and "certificate").
Therefore, the profile objects can be grouped in arrays. As only one extension of the list is required, these extensions MUST NOT be marked as optional or required:
"jsonapi": {
  "version": "1.1",
  "profile": [
    {"href": "http://jsonapi.org/ext/revision", "namespace":"rev", "status":"required"},
    {"href": "http://jsonapi.org/ext/secondextension", "namespace":"2nd", "status":"optional"},
    [
      {"href": "http://jsonapi.org/ext/auth-credentials", "namespace":"cre"},
      {"href": "http://jsonapi.org/ext/auth-certificate", "namespace":"cer"}
    ],
  ]
}

TODO: The array to group extensions doesn't allow to add properties to the group itself. This could be required in future versions... Use an object here to avoid later compatibility problems?
Marking deprecated extensions
If the server switches to a new (required) extension, but still wants to support the old (required) extension (to not break clients), both can be grouped and the old extension can be marked as deprecated. Only grouped extensions can be deprecated.
In this case, the client SHOULD prefer a non-deprecated extension if possible.
"jsonapi": {
  "version": "1.1",
  "profile": [
    {"href": "http://jsonapi.org/ext/revision", "namespace":"rev", "status":"required"},
    {"href": "http://jsonapi.org/ext/secondextension", "namespace":"2nd", "status":"optional"},
    [
      {"href": "http://jsonapi.org/ext/auth-credentials", "namespace":"cre", status:"deprecated"},
      {"href": "http://jsonapi.org/ext/auth-certificate", "namespace":"cer"}
    ],
  ]
}

Which proposed extensions can be implemented as a profile extension?
JSON-LD
The proposal for an JSON-LD extension cannot be realized, because the current base spec doesn't allow the "@" character in key names, which is required here. So the base spec would have to be extended first or we need to define a way to map key names like in the following example:
"jsonapi": {
  "version": "1.1",
  "profile": [
    {"http://jsonapi.org/ext/json-ld" namespace="ld"}
  ],
  "keymap": {
    "ld:at-id": "ld:@id",
  },
}

This would be a way to bypass all key name limitations, using valid member names and without influencing the JSON API functionality - you can still sort and filter using the key placeholder. If you want to handle the data as JSON-LD, you need to replace the keys with the values in the key map (and remove their namespace prefixes).
TODO: Check more suggested extensions!
Background: Decision for namespaces
There are different solutions on how to avoid conflicts with keys that are added by extensions. Conflicts between extensions and future versions of the base spec using the same key, and conflicts between multiple extensions or extension versions using the same key.
All agree, that allowing all keys within one global namespace will cause problems sooner or later. This must be avoided. The solution should also meet the following conditions as good as possible:

It should be easy to implement on server side and especially on client side.
It should be future proof and solve all possible conflicts, also edge cases.
It should be possible to add a new extension without breaking clients.
It should be possible to upgrade an server to a new base spec without breaking clients.
It should be possible to upgrade an server to a new extension version without breaking clients.
It should be possible, that extension functionality is implemented into the base spec, with the option to modify it.
It should be possible to switch to the functionality of a new base spec that exactly replaces a previous extension, without breaking the client.
If the functionality of a new base spec that replaces a previous extension contains any changes (which includes the document format, but also the handling of special cases like errors etc.), the change should not break clients.
All features defined in the base spec must be usable with extensions (fields, sort, filter).
The resulting document should be easy to read and understand.
The resulting document should be as small as possible.

Implicitly Qualified Names
The last version of "implicitly qualified names" suggested by @ethanresnick is very similar to using namespaces, but removes prefixes, if they do not conflict with other keys. This makes the document more compact (and better readable if all prefixes can be avoided):
 {
  "jsonapi": {
    "version": "1.1",
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "key": "r"},
      {"href": "http://jsonapi.org/extensions/last-modified", "key": "m"}
    ]
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "links": {
      "r:last": "http://example.com/revisions/1.1",
      "modifications": "http://example.com/revisions/1/mods"
    },
    "revision": "2",
    "r:last-modified": "2015-11-15T07:08:00.000Z",
    "m:last-modified": {
      "modification-datetime": "2015-11-15T07:08:00.000Z",
      "modification-user-id": "123"
    }
  }]
 }

Notes

The namespace separator used here (":") is just an example. In the final version every character except "." (U+002E PERIOD) can be defined as separator, as long it's not allowed in member names.
The namespace is defined by the API, so the name is always the same for clients.

Advantages (regarding the mentioned conditions)

With this solution extensions can extend the base spec and other extensions. To avoid conflicts, the server needs to now all keys used by the base spec and the used extensions, to decide which extension keys need to be prefixed.
If the new base spec doesn't contain keys that are in use by extensions (which can be avoided by the planned extension registry) and is compatible to the previous base spec (which is the intention of JSON API), the server can be upgraded to a new base spec at any time without breaking clients. All extension will work with the new base spec.
The base spec can implement the extension functionality either as is or with modifications of the extension spec.
Features like fields, sort and filter can also be used with prefixes. All keys can directly be addressed, but it's a bit more difficult than in the "Fully Qualified Namespaces" solution, because new base spec or extension version and newly added extensions will also change the behavior of these features. The the required modifications are perhaps bigger than expected.
The resulting document only uses prefixes if required to differ same key names. Therefore it's always smaller than the namespaced variant.

Disadvantages (regarding the mentioned conditions)

The implementation is generally simple, but tricky in some cases. Keys can be used like base spec keys, except that the namespace prefix has to be added to some keys. The user needs to know which keys need to be prefixed. Some languages might have problems with the used namespace separator, but this topic has been discussed before and is not relevant for the spec. Every language should be able to set valid JSON keys.
Extensions that are added can create conflicts. E.g. if the "last-modified" extension wasn't present in the first version, the "last-modified" key of the "revision" extension would have been unprefixed. When the "last-modified" extension is added later, both "last-modified" keys must be prefixed, which means, that this would break the previous functionality for all clients (as they cannot access "last-modified" anymore, but have to use "r:last-modified" now).
When an extension is updated to a new version, this new version can introduce new keys (which is probably the case for most extension updates). These new keys can conflict with the keys of other extensions already in use, so that the keys of both extensions need to be prefixed (prefixing only one is not possible). This can break clients.
Clients that use an extension functionality which is exactly replaced by a new base spec, can require a small modifications of the client. E.g. if the "last-modified" extension in the example above is standardized, the key is prefixed in the current implementation. So the prefix needs to be removed. Such modifications will be rare, but if they are required, this can break clients, because the new functionality cannot be used without modification and if the replaced extension contains other keys that are now used by the base spec (the "modifications" link in this example), this would now have to be prefixed ("m:modifications"), resulting in a required change on client side.
If the new base spec implements a modified version of the extension's functionality, the clients still need to use the extension. This should always be compatible with the new base spec, but it will result in document changes, because some extension keys now have to be prefixed. This will break clients, because they need to used the prefixed keys now - or implement the modified functionality. The modification and the mixing of prefixed and unprefixed keys can result in (security) problems as described in RFC6648.
The resulting document is easy to read, but a bit difficult to understand. Prefixed names are mixed with unprefixed names. If unprefixed, you don't know which keys belong to the base spec or to which extension. So you have to know each spec (base spec and extension specs) in the currently used version very well to really understand the document.

Fully Qualified Namespaces
The use of fully qualified namespaces would give every extension an own namespace, so there is no technical limitation in keys that can be created by an extension (but it's still possible to require their registration and limit them). Some namespace implementations can make things complicated, e.g. the XML namespaces) with their "namespace defaulting" or CSS namespaces, that allow selectors (filters) that match multiple namespaces. We don't use these features:
 {
  "jsonapi": {
    "version": "1.1",
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "namespace": "r"},
      {"href": "http://jsonapi.org/extensions/last-modified", "namespace": "m"}
    ]
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "links": {
      "r:last": "http://example.com/revisions/1.2",
      "m:modifications": "http://example.com/revisions/1/mods"
    },
    "r:revision": "2",
    "r:last-modified": "2015-11-15T07:08:00.000Z",
    "m:last-modified": {
      "m:modification-datetime": "2015-11-15T07:08:00.000Z",
      "m:modification-user-id": "123"
    }
  }]
 }

Notes

The namespace separator used here (":") is just an example. In the final version every character except "."  (U+002E PERIOD) can be defined as separator, as long it's not allowed in member names.
The namespace is defined by the API, so the name is always the same for clients.

Advantages (regarding the mentioned conditions)

The implementation is simple. Keys can be used like base spec keys, except that the namespace prefix has to be added. Some languages might have problems with the used namespace separator, but this topic has been discussed before and is not relevant for the spec. Every language should be able to set valid JSON keys.
With this solution extensions can extend the base spec and other extensions. Every extension has its own namespace so conflicts are impossible.
Extensions can be added at any time without creating conflicts. Clients only have to support the extension but don't need to change anything, as long as the API explicitly requires it.
The server can be upgraded to a new base spec at any time without breaking clients, as long as the new base spec is compatible to the previous version (which is the intention of JSON API). Extension keys are namespaced, so all extensions will work with the new base spec.
An extension can be updated to a new version without breaking clients, as long as the next extension version is compatible to the previous version.
The base spec can implement the extension functionality either as is or with modifications of the extension spec.
Clients that use an extension functionality which is exactly replaced by a new base spec, always require a small modifications of the client, because the namespaces of the extension keys need to be removed. This isn't always as easy as in the "Implicitly Qualified Names" solution, but it's always guaranteed that the update to the new base spec doesn't break clients. Although a modification is required, the client has enough time to implement it, because the extension can still be used with the new base spec without modifications.
If the new base spec implements a modified version of the extension's functionality, this doesn't break clients, because the previous extension should always be compatible with the new base spec and therefore can be used without changes.
Features like fields, sort and filter can also be used with namespaces. All keys can directly be addressed.
The resulting document is easy to read and understand, because all base spec keys are unprefixed and all extension keys are prefixed. So you exactly know which key belongs to which extension.

Disadvantages (regarding the mentioned conditions)

The resulting document is bigger than the "Implicitly Qualified Names" variant, because it always uses namespace prefixes - but when the document is gzipped (which should be the case in most implementations), the difference is insignificant.

Additional possibilities

Extensions could be marked as optional. e.g. if they are only informational (like the "last-modified" extension in the example above). Clients don't have to explicitly support optional extensions, it's enough that the consequent namespacing allows the client to identify and filter out all keys that belong to this extension. This would improve client compatibility, because optional extensions can always be added without breaking clients.

Result
The "Implicitly Qualified Names" solution avoids the use of unnecessary prefixes and make the switching from the extension functionality to a later implementation of the functionality in a new base spec easier, but only if all conditions are met. And this is the problem here: You can try to avoid conflicts by requiring the registration of extensions and all their members, but nevertheless you can never guarantee that the described conflicts cannot occur.
Of course such conflicts should never occur (this will always be our intention), but you have to think long-term. The extension system must be solid enough to handle such cases, every future extension will depend on it. So I focused on the stability/extendability of the JSON API standard and the API update process. A secure update process that doesn't break clients is be the best argument for using JSON API - if this cannot be guaranteed, an API provider will perhaps prefer another format.
One could say that this stability has the price, that servers and clients are not updated to use the functionality of new base specs, because the old extensions still work. That's possible, but this is also a great advantage. APIs can upgrade to a new base spec, integrate the new functionality later, offer the new or the old functionality in parallel (depending on the client maximum version, sent with the request), and finally suspend the old functionality. This takes longer, but is a clear update process that doesn't break clients. The "Implicitly Qualified Names" is more radical here and an update of the server can break clients, which forces them to upgrade too - but if you run an API and have to decide if you upgrade it to the new spec, would you do it in this case? I would prefer not to change anything as long as possible and require new clients to support the old spec, so the new base spec will perhaps never be adopted.
So the "Fully Qualified Namespaces" is the clear winner here. It offers great flexibility for the base spec and extensions, best stability and updatability and a clearer document structure. The mentioned addition of optional extensions would further reduce potential compatibility problems with old clients and give API providers more freedom to extend their API.
Of course the extension registry can still be required, although it's no more necessary when using namespaces. Perhaps it will be optional, to be listed as an "official" extension, that has been publicly reviewed and follows given standards.

@ziege I'm just seeing this, but it may take me a while to get back to it. I'll try to respond by the middle of next week.

Edit: it may actually take another couple days, since I want to put together a thoughtful + thorough response to this, but I will post one soon!

@ethanresnick No problem, I also had to read and check it again and again, to come to a more or less coherent solution (this was version 7). And I'm still not sure about all points. At the same time I create a test implementation for it, to be sure that I didn't miss anything - the validator already works great with profile extensions, and I'm currently working on the server...

@ethanresnick Did you have the time to look into this? It's much different than the current experimental extension suggestion, e.g. that the Accept header is used for the extension negotiation, instead the Content-Type, but when you check the examples (GET requests, multiple versions, parallel supported extensions,...), this makes sense.
As I said, I'm currently working on a server. The only problem I had was parsing the media type parameters (all tested libs did it wrong and had problems with commas in the value, so I had to write this part too). For the OPTIONS request I could also think of a request to the API root (= no resource), if this is seen as a potential problem for some servers or as the generally preferred way.
So while working into this direction the last days and testing more use cases I could not find a problem with the suggested extension implementation.
Would be great to get some feedback- also from someone else. Thanks!

Hey @ziege! I have been looking at this, but my response is getting rather long and I haven't had time yet to finish it. (I've also been traveling the last few days.) I'll try to have it ready soon :)

@ziege First, thank you for putting together such a thorough proposal, Christoph! And sorry that it's taken me so long to get back to you. I was busy with work, and I've been traveling for the past couple weeks, but I've got a bit more time now.
Your proposal raises a number of major issues, which I’ll try to respond to in turn.
Basic Assumptions
In the "Processing newer versions" section, you propose two rules: that unknown keys in a JSON API document should be ignored by the recipient, and that invalid values should be explicitly rejected when encountered by a server.
I agree 100000% with these rules, and will write the rest of this comment as though they exist in the spec. For what it's worth, I think the first rule (about ignoring unknown keys) is in the spec already:

Client and server implementations MUST ignore members not recognized by this specification.

(Although, there's some room to clarify what it means for a member to be "not recognized".) And I think the second rule, about erroring on invalid documents (for servers and clients), is strongly implied. However, as #766 points out, it isn't specified explicitly—and we should fix that.
So we have to make these rules a bit clearer, but they are present to a large degree, and I think they should be able to be clarified where necessary in a compatible way.
Versioning & Extensibility
Definitions
First, let me provide a few definitions so that we have a shared language. These definitions are pretty standard (i.e., I’m not just making them up); for example, they're used in some W3C architecture findings. I'd suggest reading those findings directly (or this blog post), but I'll try to summarize the key points in my response below. Anyway, the definitions:

Message
Data sent between two parties. In JSON API's context, the message is the JSON API document being exchanged, the HTTP method in use, and the query parameter data. The non-query parameter part of the URI doesn't count as part of the message since it's opaque to JSON API. And, in many contexts, the HTTP method and query parameter data aren't relevant, so I'll sometimes talk about the message below as if it's only JSON API document.

Producer
The author of a message. This could be the client or the server. For example, the client clearly produces a message when it sends a PATCH request.

Consumer
The recipient of a message. Again, this could be the client or the server.

Langauge
Two sets of messages: the defined text set and the accept text set (explained below). JSON API is a language.

Defined Text Set
The set of messages that have a defined meaning. So,

{"data": { /* some resource object */} }

is the JSON API defined text set. In JSON API, a producer can only legally produce messages from the defined text set.

Accept Text Set
The set of all messages that a consumer must process without crashing. So,

{"data": { /* some resource object */ }, "magic": false }

is in the accept text set (thanks to JSON API's must ignore rule) even though it isn't in the JSON API 1.0 defined text set. For the language to work, the accept text set must (obviously) contain every message in the defined text set; otherwise, consumers won't be able to understand certain messages.

Extensibility gap
The "gap" between the defined text set and the accept text set. More precisely, the set of messages that are in the accept text set but not in the defined text set. If this set is non-empty, then some new messages can be defined without breaking existing consumers (which already accept those messages). This gap is what makes compatible extension possible, and it's why the must ignore rule is so important.

Backwards compatible extension
An extension to a language such that every message in the original language's defined text set is part of, and has a non-contradictory meaning in, the new language's defined text set. The extended language is compatible with existing producers but might break existing consumers. For example, consider extending JSON API so that a client can send an array in "data" to trigger a batch operation. Existing producers (clients) that just send single resource objects would still be compliant, but existing consumers (the servers) that don't expect an array would break.

Forwards compatible extension
Conversely, a forward compatible extension works with existing consumers but might break existing producers (i.e. prevent them from sending valid messages). For instance, if we extended JSON API such that every JSON API document needed to contain some information (say, a "revision"), some current producers would be non-compliant, but all existing consumers would work (and just ignore the new info). Formally, a forwards compatible extension is one such that every message in the extended language's defined text set is part of and has a compatible meaning in the original language's accept text.

Fully compatible extension
An extension that is both forwards and backwards compatible.

Your Proposal
You say:

[From one minor version to the next,] each document or request following the old spec must still be valid in the new spec and handled like in the old spec. A document or request following the new spec doesn't have to be valid in the old spec.

So, if I'm understanding you correctly, you're saying that a minor version bump can break existing consumers but cannot break existing producers. Or, put differently, a minor version bump can happen when an extension is backwards compatible but not fully compatible.
 The smaller ones are:

Why did you choose to allow backwards-compatible changes within the same major version rather than forwards-compatible changes? On the surface, at least, it's not obvious why one type of changes should be preferred over the other (when a negotiation mechanism is already being used, and servers and clients can both be consumers and producers). In fact, I'm thinking the opposite model would be more useful, if we're going to have breaking changes.[1]

When the base spec, or an extension, is extended in a fully-compatible way,[2] would that trigger a minor version bump? Or do minor version bumps only occur if the change is backwards compatible but not fully compatible? (I'd lean toward the latter, for reasons alluded to in the discussion below about maximizing interoperability.)

But my biggest question is: are you sure that the costs of allowing any incompatible changes justify the benefits?
Imagine a world in which the base spec, and every extension, always evolve in a fully-compatible way. [This may be a bit unrealistic, but take it as a hypothetical.] There might be version numbers for humans, as a convenient way to refer to a particular iteration, but those version numbers wouldn't play any part in how a message is produced or processed. Instead, since full compatibility is guaranteed, every producer would simply author messages that are as rich as it know hows to create, and every consumer would just extract as much meaning from the message as it can, without either party invoking conditional rules based on the version number.
Compared to the world described above, every time we make an incompatible change, we add some complexity, or we lose some interoperability, or both. In the model you proposed, with backward-but-not-forward compatible changes between minor versions, the extra complexity comes because producers have to introduce conditional logic to generate valid documents for old consumers, and the loss of interoperability comes when producers simply stop supporting old consumers. In the opposite versioning model, with forward-but-not-backward compatible changes between minor versions, the extra complexity would come from consumers having to add conditional logic or drop support for old producers.
Also, if we allow incompatible changes, we have to have define a way to negotiate on based on version (i.e., the initial OPTIONS request in your proposal). This negotiation isn't too hard, but it is an extra step that will likely trip some developers up. Perhaps more importantly, version negotiation means that many variations of essentially the same document could exist, which is bad for caching (and therefore particularly bad for RESTful APIs, which rely more on caching to mitigate latency issues that come from their more-verbose messages).
So the question becomes: if we introduce any breaking changes, what do we get in exchange for the loss of interoperability and/or the increased client and server complexity? The answer, of course, is that we get an opportunity for a "do over"; that is, an opportunity to fix or extend the design in ways that wouldn't have been possible without the break.
I know how useful such breaking changes can be: I've spent a ton of time in the Javascript community, which is intent on never breaking existing producers, and so I've felt the difficulties associated with ruling out breaking changes. And I don't necessarily want to rule them out forever for JSON API. But my point is that it's not an automatic conclusion that allowing some type of breakage is better than not. Breaking changes have downsides, and JSON API's lack of versioning strategy to date has been due to (largely @steveklabnik's, I think) awareness of this; it's not simply an omission.
When/if we add support for breaking changes, which we can always do later in a similar way to what you're proposing now, we'll have a thorough discussion about the best way to do it. And we might end up at your proposal, but who knows.[3]
For now, though, I want to halt discussion of versioning/making incompatible changes to the base spec, since figuring that out isn't required to launch profile extensions, and we need to keep the scope of this issue as narrow as possible if extensions are actually going to get done.
For versioning extensions, it does make sense to me that extensions should have somewhat more freedom to break compatibility, at least during their early development, than the base spec has. But, I want to point out that, if an extension needs to change in a way that's not fully compatible, that change can be made just by creating a new URI, without any versioning mechanism. This URI would be associated in humans' minds, and in the registry, as a new version of the old extension, but the two versions would be independent from the computer's perspective. For instance, the client and server would reference "http://example.com/ext/last-modified/v2" instead of, or in addition to, "http://example.com/ext/last-modified" in their profile` media type parameter. My gut says that the above strategy is sufficient, though I'm open to arguments that it isn't.
Definition of a Profile Extension
I agree with almost all of your rules for what constitutes a profile extension, and I'm glad to see them spelled out explicitly. I wonder about these three things, though:

Do profile extensions really need to be able to add new HTTP headers? Maybe, during the beta period for 1.1, we should not allow header information to be part of extension processing, and then we can change that if an extension comes along that has some compelling reason to require a header?

I like the idea of forcing profile extensions to be order-indepedent, but I'm not sure it's feasible. As I think you pointed out on discuss, this would make it impossible to implement a request signing extension, for example. Are there other rules we can come up with that would allow some order-based processing but help minimize problems with applying multiple extensions?

Re whether extensions should be allowed to re-define the status code of a server response for specific situations… I think extensions should be able to trigger 4xx responses if they use invalid data, but other than that I'm not sure. I'm inclined to say that we should leave this ambiguous for now and figure it out during the beta period too.

Extension Negotiation
Once base spec versioning is off the table, and if we decide to do extension versioning in the way described above (just minting a new extension URI for when there's an incompatible change), then the need for any negotiation with OPTIONS goes away. As I said in my original post:

Either party (the client or the server) can send extended data and, if the recipient doesn't know how to interpret some or all of the extensions, it'll just ignore the ones it doesn't understand. Beautiful! If a server wants to minimize unnecessary data transfer, it can choose to send data for a profile extension only if the client indicates that extension in its Accept header.

I added that: "if a client wants to check to do the same, it can perform an OPTIONS request first...and only send data for supported extensions."[4] But clients don't need to be able to do this, strictly speaking (not having it just means a bit of extra data transfer).
Of course, your OPTIONS response is doing a couple other things too, like listing deprecated extensions and extensions the client must produce in their messages. All of these are very useful, so I totally support having an endpoint that returns this information. But, for the minimum viable extension system, I don't think we need to figure out this response just yet; we can always add it later.
For now, we should just think about:

Is the server allowed to require that the client include any extension, or should it only be allowed to require certain extensions (e.g. those for security)? If it is worth limiting which extensions the server can require the client to produce, how should we set those limits?

What should the error should look like if the client neglects to include an extension the server requires? (We'd need to define this error even if we had an OPTIONS response, of course.) The error is clearly a 415, but should it have any special structure? E.g. a standard way to indicate which extension was missing or a list of extensions that would fulfill the requirement? (Adding those extra details starts to duplicate your OPTIONS stuff, but defining these things in the error response also seems like it would be worthwhile, even if we did have an OPTIONS-like response, since the client shouldn't have to make an extra request necessarily to get this info.)

For the error format, I was thinking maybe something like this:
{
  "errors": [{
     "status": "415",
     "source": {
       "pointer": "",
       "missing": {
         "key": "http://jsonapi.org/ext/password-auth",
         "alternatives": ["http://jsonapi.org/ext/facebook-signin"]
       }
     }
  }]
}

In the above example, "pointer" points to the value in the request document that triggered the error (as usual). Here, it's the document's top-level that triggered the error, because the top level was missing the required password extension data. But, the "missing" key complements "pointer" to say that the reason that that pointed-to value triggered an error was because it was missing a key, and the key it was missing is the one identified by the URI in "key".
What I like about the above is that it generalizes to other errors that don't have to do with missing extensions. For instance, we have this on our examples page:
PATCH /posts/1 HTTP/1.1

{ "datum": [ ] }

and the response is
{
  "errors": [
    {
      "source": { "pointer": "" },
      "detail":  "Missing `data` Member at document's top level."
    }
  ]
}

If we had a "missing" member, the response could be something like:
{
  "errors": [
    {
      "source": { "pointer": "" },
      "missing": {
        "key": "data", // maybe this'd be a URI??
        "alternatives": ["meta"]
    }
  ]
}

Again, there'd be a bunch of details to figure out re the above proposal, and it may not be the right approach at all; it's just a rough sketch. Thoughts?
For the above proposal to work, there'd need to be a standard URI for not just each extension's top-level key (which is just the extension's URI), but also for its "exported keys" (i.e. those usable in other extensions' payloads). Having such URIs is a good idea anyway, though, since one problem that's been documented in XML—and that we don't have to replicate here—is that there's no standard mapping between a {namespace URI, local name} pair and a URI.
Misc

Extensions SHOULD avoid to use key names (within own objects) that are already used by the base spec, except the values of these keys are taken from the base spec (no matter if prefixed/namespaced or not). This increases the chance that an extension can be integrated into the base spec without noteworthy changes later.

I like this! I've updated my original post to add it as part of the registration requirements.

Extensions can require a specific base spec version (e.g. if it requires a specific object, that is available from a specific base spec version). The requirement has to be defined in the extension spec and considered when implementing it on server/client side.

I’m fine with putting this in the extension’s registration (just for convenience of developers browsing extensions), but I don't think it needs to effect processing at all, as I described earlier. I've updated my original post to add this as a registration requirement too.

The proposal for an JSON-LD extension cannot be realized, because the current base spec doesn't allow the "@" character in key names, which is required here.

Right, so we also need to loosen the member name restrictions so that an "@" is allowed. And that's ok: we reserved these keys at least in part so that we could release them later for specific new needs. But giving up JSON-LD isn't acceptable to me, and that's one argument in favor of (something like) implicitly qualified names, which does support JSON-LD after the member naming requirements are loosened.
Implicit vs Explicit Namespaces
Almost all of the problems you identified with implicitly qualified names stem from misunderstandings around how they're supposed to work. To me, that suggests a larger issue: implicitly qualified names may just be too confusing. If you are confused by them, I bet every other developer will be too. So, I'll try to clear up the confusion below, but then I also want to suggest a different scheme that may be less confusing.
First:

Extensions that are added can create conflicts. E.g. if the "last-modified" extension wasn't present in the first version, the "last-modified" key of the "revision" extension would have been unprefixed. When the "last-modified" extension is added later, both "last-modified" keys must be prefixed, which means, that this would break the previous functionality for all clients (as they cannot access "last-modified" anymore, but have to use "r:last-modified" now).

What this isn't taking into account is that every extension only stores its data under one member in my proposal (the name given in the "key" in the profile link). The only exception is for extension-provided members that are explicitly allowed (as part of the registration) to exist outside of the extension's key—and these latter type of members are always prefixed. So, when only the revision extension is used at first, the document would look like this, assuming the revision extension explicitly was registered to allow its "last-modified" key to exist outside its main container:
{
  "links": {
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "key": "revision"}
    ]
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "revision": "2",
    "revision:last-modified": "2015-11-15T07:08:00.000Z"
  }]
}

More likely, though, since putting keys outside the main container would be discouraged unless there's a compelling reason, the document would instead look like this:
{
  "links": { /* same as above */ },
  "data": [{
    "id": "1",
    "type": "resource",
    "revision": {
      "tag": "2",
      "last-modified": "2015-11-15T07:08:00.000Z"
    }
  }]
}

Then, when the second, similar extension is added, the document would look one of two ways. If both extensions were registered with "revision" as their "suggested key" (which would be the goal, to encourage the community to pick just one), the document would be:
{
  "links": {
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "key": "revision"},
      {"href": "http://jsonapi.org/extensions/revision", "key": "_revision2"}
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "revision": {
      "tag": "2",
      "last-modified": "2015-11-15T07:08:00.000Z"
    },
    "_revision2": {
      "modification-datetime": "2015-11-15T07:08:00.000Z",
      "modification-user-id": "123"
    }
  }]
}

If the extensions were registered with different suggested keys, the version with both extensions would instead look like:
{
  "links": {
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "key": "revision"},
      {"href": "http://jsonapi.org/extensions/revision", "key": "last-modified"}
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "revision": {
      "tag": "2",
      "last-modified": "2015-11-15T07:08:00.000Z"
    },
    "last-modified": {
      "modification-datetime": "2015-11-15T07:08:00.000Z",
      "modification-user-id": "123"
    }
  }]
}

(Or, the "last-modified" member in "revision" might exist outside, as "revision:last-modified"; either way, there's no conflict.)

When an extension is updated to a new version, this new version can introduce new keys (which is probably the case for most extension updates). These new keys can conflict with the keys of other extensions already in use, so that the keys of both extensions need to be prefixed (prefixing only one is not possible). This can break clients.

This is not right for the same reasons discussed above. In other words, when the extension is updated, it will either add new members only within its own key (where there's no risk of conflict) or, it will add new members that are allowed to cross extension boundaries—but those are always prefixed so don't create conflict.

Clients that use an extension functionality which is exactly replaced by a new base spec, can require a small modifications of the client.

Incorporating an extension exactly as defined into a new version of the base spec isn't something we would do. It has pragmatic drawbacks (see the paragraph in my prior post that begins "If a feature gains popularity as an extension, and we then standardize that feature in the base spec…"), which is a big part of the reasoning behind the implicitly-qualified names proposal in the first place. But, also, it wouldn't even be allowed, because the proposal prevents the base spec from adding keys that already have registered extensions using those keys.

If the new base spec implements a modified version of the extension's functionality, the clients still need to use the extension. This should always be compatible with the new base spec, but it will result in document changes, because some extension keys now have to be prefixed.

Again, the base spec could never add a key that's already used as some registered extension's key, so no prefixing is necessary.
Alternate namespacing idea…
The basic logic behind implicitly qualified names is sound in my opinion, but the confusion is coming from the "key" member, which makes people think that the proposal works more like XML namespaces than it really does. I'm going to try to write up a version of the idea soon that doesn't use the "key" member and that gives us a unified approach for extension namespacing and CURIEs in the "links" object (for when we expand that to match RFC 5988).
Conclusion/Next Steps
Figuring out how to manage breaking base spec changes is a really valuable-but-complicated topic, and your ideas will be very useful when we tackle that problem. But, to just launch profile extensions, we don't need to worry about that for now; nor, even, do we need to worry about the OPTIONS-style discovery response. So let's put those aside. Instead, I'd like your thoughts on a few specific questions:

For versioning extensions, is it sufficient to just make a new URI when there's a breaking change?

Are there better ways to specify extension processing-order requirements?

Should every extension be able to be required by the server, or only certain ones?

What should the error response for a missing extension be?

Ok, that's it. Thanks again Christoph!!
Footnotes

Forwards-incompatible changes (i.e. those that are allowed to break existing consumers) are pretty rare, because the must-ignore rule makes existing consumers very resilient. So the only time you really need to do it is to remove a required member or change the format of an existing member. But removing a required member also breaks producers, so the only time that only consumers break is when the types of values that a member is allowed to contain is expanded. So, if a member could before take only a string is now allowed to take either a string or an array, that breaks consumers. However, there's always a fully compatible way to achieve the same effect: just define a new member that can take the expanded set of formats (and allow producers to continue including the old member as a fallback). This is less elegant than simply breaking existing consumers, but it's a pretty rare thing to need to do. There are also other, opt-in solutions (like allowing the client to send a media type parameter in its Accept header to indicate it supports the expanded range of values, which the server can then return) that are more-targeted than adding a versioning system.

That's why I say that most of the benefits of breaking changes come from allowing backwards-incompatible changes (i.e. those that make existing producers non-compliant). These changes make it possible to require some data be present that wasn't previously (very useful) or to the forbid use of some old feature. The set of defined messages is always very easy to expand—just add a new, optional member—but doing that just makes the contract fuzzier and fuzzier over time as more optional features come around. It's only with backwards-incompatible changes that the defined text set can be narrowed. Ahd that narrowing can be very helpful for keeping the specification easy to learn and implement.

For example, suppose we allow an "href" key in resource identifier objects that could be ignored by old consumers. (This clearly wouldn't be a patch.)

For instance, we might want a way for servers to pick and choose which new features they implement, whereas a linear versioning scheme dictates that if a server is implementing 1.4, it's also implementing all the new features added in 1.1–1.3. Version numbers can also have other ambiguities if a JSON API document is encountered outside of the protocol. For example, some 1.4 documents might be valid 1.1 documents, but others wouldn't. To communicate this, a document would probably want to identify itself as a 1.1 document in the document's body, which is distinct from saying JSON API version 1.4—the protocol—is in use. (See here for more on this last problem, and other approaches to it.)

The only part I'd disagree with from my original comment above is that I'd now prefer, because of OPTIONS' downsides, that the server send a GET request to some URI, either provided in "links" or memorized by the client. But the idea is the same.

@ethanresnick Thanks for your detailed reply. It will take some time to go through it. That's why I want to begin with your questions regarding the next steps, so that we can go on with our thoughts:

For versioning extensions, is it sufficient to just make a new URI when there's a breaking change?

I think this is at least theoretically possible.
What I'm missing here is how the communication between client and server works in your proposal. How does the client inform the server about its supported extensions (also for GET requests) or how can the client get the requirements from the server?
And how do you enable the server to switch to a new (incompatible) extension without breaking clients? I think this is most important!

Are there better ways to specify extension processing-order requirements?

Do you think that this still matches the definition of a "profile" extension?
A signing extension for example can get very complex and does more than just adding something. Such an extension should be realized as a comprehensive extension, for which a specific processing order could be defined.

Should every extension be able to be required by the server, or only certain ones?

Some extensions only add information (e.g. a "last-modified" extension) that is optional. The consumer (client in this case) can support the extension (and work with the information or not) or simply ignore the additional fields.
A new version of an extension can introduce new functionality, so that it's not only informational anymore. So a "revision" extension can start with additional information only and later introduce new functionality to check for the correct revision of request documents (which then makes sense to be required by the API).
I don't see a problem in allowing all extensions to be defined as required. Why not keep it simple and let the API provider decide it? I'm sure no API will define an informational extension unnecessarily as required.
A special case to consider: How to handle the case, that it's required to support an extension from a given list of extensions? (See the example in my proposal - an API could support multiple extensions to realize the client authentication, but the client has to support only one of them. Defining all as optional or required would be wrong here.)

What should the error response for a missing extension be?

If I understand you correctly you try to avoid the (perhaps unnecessary) OPTION request and instead return an extended error that helps the client to understand the server requirements (and perhaps handle them automatically). Is that right?

Thanks @ziege!

That's why I want to begin with your questions regarding the next steps, so that we can go on with our thoughts:

:+1: Let's keep this moving! So, to answer your questions quickly....

I think this is at least theoretically possible.

Cool :)

How does the client inform the server about its supported extensions (also for GET requests)

Through the Accept header. So the client says: Accept: application/vnd.api+json; profile="http://jsonapi.org/ext/1 http://jsonapi.org/ext/2 http://jsonapi.org/ext/3" and just lists all the extensions it supports in the profile parameter. (This is the same as in #650.)

how can the client get the requirements from the server?

For now, what I'm proposing is that the client can't get the requirements upfront. Instead, if the client omits a requirement, the server sends an extended error listing all the missing extensions. And then the client adds those extensions and retries. This isn't ideal—which is why I support adding an OPTIONS-style response later for discovery—but I think it's good enough to launch with.

And how do you enable the server to switch to a new (incompatible) extension without breaking clients? I think this is most important!

I'm not sure I understand... if the server introduces a new requirement, that will by definition break clients, right? Do you mean: how can the server have usage data so that it knows when its reasonably safe to remove an old required profile extension? If so, then the answer is that it can support two extensions in parallel for a while, and log the Accept header data. Once a large enough percentage clients list the new extension in that header, it can remove the old one.

Do you think that [an order-specific extension] still matches the definition of a "profile" extension?

Ahh, I hadn't thought about that. In the name of launching quickly, I'd be very happy to say that an order requirement makes something into a comprehensive extension! That would mean signing is off limits for now, but I think that's ok.

I don't see a problem in allowing all extensions to be defined as required. Why not keep it simple and let the API provider decide it? I'm sure no API will define an informational extension unnecessarily as required.

I guess this is fine. The only reason not to do it would be that sometimes people think something needs to be required but, if you force them to be a bit more creative, they can actually find a way to make it optional. And, of course, making things optional is good for interoperability (since a generic client clearly won't know how to satisfy most requirements). Maybe the solution here is also to have a beta period to see how things play out?

A special case to consider: How to handle the case, that it's required to support an extension from a given list of extensions?

This goes back to the error format that I discussed in my previous post. Let's say that one of the following extensions is needed: http://jsonapi.org/ext/a, http://jsonapi.org/ext/b, http://jsonapi.org/ext/c, or http://jsonapi.org/ext/d. Then, if the request includes none of them, the error would be something like:
{
  "errors": [{
    "status": "415",
    "source": {
      "pointer": "",
      "missing": {
        "key": "http://jsonapi.org/ext/a",
        "alternatives": ["http://jsonapi.org/ext/b", "http://jsonapi.org/ext/c", "http://jsonapi.org/ext/d"]
      }
    }
  }]
}

Above, the server says that Extension A is missing but also that B, C, or D would be acceptable alternatives to providing A. Any of the extensions could be marked as being the "missing" one (as opposed being an alternative); but, if it does prefer one extension (e.g. because the others are deprecated), that's the one that would most-sensibly go in "key".
Again, don't focus too much on the names of the members in the error above—it's just a sketch—but that's roughly how it could work.

If I understand you correctly you try to avoid the (perhaps unnecessary) OPTION request and instead return an extended error that helps the client to understand the server requirements (and perhaps handle them automatically). Is that right?

Yup, exactly.

Thanks, this sounds good, but I think the following points needs to be adjusted:

Through the Accept header. So the client says: Accept: application/vnd.api+json; profile="http://jsonapi.org/ext/1 http://jsonapi.org/ext/2 http://jsonapi.org/ext/3" and just lists all the extensions it supports in the profile parameter.

A good client should support a huge list of extensions, let's say several hundred in the future... So this list would get really long and produce much overhead. :) I think the client should send a list of extensions it wants to use instead.
The possible/required extension can be defined in the API docs or read from the error response after the first request.
The current error response for missing extensions doesn't allow to list multiple missing extensions (in one error object). Why not combine the error with the proposed OPTIONS response?
{
  "errors": [{
    "status": "415",
    "requirements": {
      "profile": [
        {
          "href": "http://jsonapi.org/ext/revision",
          "status":"required"
        },
        {
          "href": "http://jsonapi.org/ext/secondextension"
        },
        {
           "href": "http://jsonapi.org/ext/a",
           "alternatives": [
             {"href": "http://jsonapi.org/ext/b"},
             {"href": "http://jsonapi.org/ext/c"},
             {"href": "http://jsonapi.org/ext/d"}
          ]
        },
      ]
    }
  }]
}

The requirements member could also be extended later for other types of requirements (e.g. "extensions").

Your questions regarding versioning

So, if I'm understanding you correctly, you're saying that a minor version bump can break existing consumers but cannot break existing producers.

No, the text you referred to is independent from the type of change.
What I wanted to say is: A new version (of an extension or the base spec) should still be able to produce and consume messages of older versions. This means that the consumer or producer can upgrade to a new version but still communicate using the previous version.
This enables the consumer/producer to upgrade at any time without worrying about compatibility problems.

Why did you choose to allow backwards-compatible changes within the same major version rather than forwards-compatible changes?

I tried to find a way to integrate extensions in a way that ensures that neither the producer nor the consumer ever breaks (using content negotiation and the OPTIONS request), so I didn't differ between backwards, forwards and fully compatible extensions the way you do.
I think it's most important, that a server can be upgraded without breaking clients.

When the base spec, or an extension, is extended in a fully-compatible way, would that trigger a minor version bump? Or do minor version bumps only occur if the change is backwards compatible but not fully compatible?

Every change of the spec requires new version number, also if it's fully compatible. I'd say that a fully compatible change is also a minor version change, but one could also say this is a patch version change only, because it can be fully ignored on consumer/producer side.
Backwards compatible changes always lead to a new minor version.

But my biggest question is: are you sure that the costs of allowing any incompatible changes justify the benefits?

I wrote that all "changes SHOULD be backwards compatible" (note: I had a different definition of "backwards-compatible", what I meant is "SHOULD NOT break anything").
But I also think we have to define what to do, if this is not the case.
In your following arguments you seem to expect incompatible changes with each version, but this should be an absolute exception. So it's very similar to your proposal, except that you move incompatible changes to new extensions (which IMHO isn't better when it comes to implementation
complexity).

Also, if we allow incompatible changes, we have to have define a way to negotiate on based on version (i.e., the initial OPTIONS request in your proposal).

Yes, but's that's also required if you switch to another extension instead. Then you have to negotiate the used extensions...
I think using versions has benefits here. The extension doesn't need to be changed and you can upgrade it without breaking clients. IMHO it's also easier to extend an extension implementation to support a new version (which will mostly be compatible with the old one) than to implement a completely new extension - which always has to be available for clients and servers before it can be used (and therefore makes it harder for them to switch).

Perhaps more importantly, version negotiation means that many variations of essentially the same document could exist, which is bad for caching

This is also true if you switch to another extension instead (and still support the old extension for some clients).

When/if we add support for breaking changes, which we can always do later in a similar way to what you're proposing now, we'll have a thorough discussion about the best way to do it. And we might end up at your proposal, but who knows.

It's okay to implement this later (because not necessary now), but it's important to be prepared for this later possibility. I think using versions (also if all changes are compatible now) would be a good base for later extensions.
Definition of a Profile Extension

Do profile extensions really need to be able to add new HTTP headers?

Perhaps your're right and that's too much for a profile extension. Testing this in a beta is a good idea. Same for the status codes.

Are there other rules we can come up with that would allow some order-based processing but help minimize problems with applying multiple extensions?

Of course you could define rules for it or the server could define rules, but it could make the implementation much harder.
A possible JSON-LD extension
You wrote that you want to allow the "@" for member names. What about my key map suggestion, which would solve this problem in a more general and compatible way?
Implicit vs Explicit Namespaces
I'm still not a fan of implicit namespaces.

extension only stores its data under one member

I think you mean "wraps its data in one member", right?
So an extension that adds fields to the top level and to resources would look like in the following example:
{
  "links": {
    "profile": [
      {"href": "http://jsonapi.org/extensions/revision", "key": "revision"}
    ]
  },
  "data": [{
    "id": "1",
    "type": "resource",
    "revision": {
      "tag": "2",
      "last-modified": "2015-11-15T07:08:00.000Z"
    }
  },
  {
   "id": "2",
   "type": "resource",
   "revision": {
     "tag": "5",
     "last-modified": "2015-11-14T09:08:15.000Z"
   }
  }],
  "revision": {
    "min-modified": "2015-11-14T09:08:15.000Z",
    "max-modified": "2015-11-15T07:08:00.000Z"
  }
}

I think the result is similar to using explicit namespace. All you do is to take the namespace and use it to wrap the fields instead of prefixing them.
One could argue that this makes the structure of the document unnecessarily complex, because of the additional object levels, but it also helps to visually group the extensions fields.

Again, the base spec could never add a key that's already used as some registered extension's key, so no prefixing is necessary.

And you think it's good to limit the base spec's ability to add new keys? With namespaces this would never be a problem.
Suggestion (I think this has been mentioned by you earlier):
Do not reserve a fixed "key" for an extension, but make it variable.
As you use the profile URI for the extension negotiation now, the keys are only used to map fields to the right extension. So theoretically any name can be used for it. It could even be different in each
request/response document.
This would allow the base spec to add any key, as long as the producer ensures that no used key is assigned to an extension.

Wow. Kudos to both @ethanresnick and @ziege for this work.
I have a bad feeling about the automatic registration procedure. Names may be cheap, but good names are not. Remember the huge wrestling and indecision over the naming of various keys pre-1.0? Pepperidge Farm remembers.
I'd like there to be at least a modicum of vetting in place, to prevent poorly thought-out extensions from snapping up the "obvious", generic keys for their respective applications. It would also help create an extension ecosystem that promotes quality over quantity, benefitting everyone.
If an approval process is considered too tedious in general, and there's some value in making a large number of not-so-great extensions available, then I'd suggest a two-tier system: anyone could register a prefixed key (e.g., acme-signature), but reserving a generic key (signature) would involve a more stringent assessment. This way, the valuable generic keys would end up with those extensions that at least have a decent probability of becoming the de facto standards for each application.
And this is just assuming that the registration is aimed at generally useful extensions and that server-specific functionality is going to stay in meta. If this is not the case, and everyone is encouraged to register their proprietary extensions, then that's an even stronger argument for the two-pronged approach.

Do not reserve a fixed "key" for an extension, but make it variable.

This is actually a great idea for a number of reasons:

It's simpler and more elegant than the "standard key" / "emergency key" scheme.
It eliminates the issue of good keys being reserved by bad extensions.
It eliminates the need for explicit namespacing even when an extension is used within an object defined by another extension (see below).
It would even enable the spec to absorb popular extensions as standard features by freezing the key.

Mixing extensions
@ethanresnick posted this example, where valid:title-case comes from another extension:
"templates:create": {
    "fields": [{
      "name": "title",
      "type": "string",
      "validate": {
        "maxlength": "50",
        "valid:title-case": true
      }
    }]
  }

We could require that only objects, not individual members, may be embedded in this way. So, "valid:title-case": true would have to be expressed as "valid": {"title-case": true}. Now, since the object keys are dynamically specified, they can also be arbitrarily complex, so the server might specify
"profile": [{"href": "http://jsonapi.org/ext/valid", "key": "ext-valid"}]

which would lead to the following representation:
"templates:create": {
    "fields": [{
      "name": "title",
      "type": "string",
      "validate": {
        "maxlength": "50",
        "ext-valid": {
          "title-case": true
        }
      }
    }]
  }

It's dead simple to avoid collisions this way without ever needing to namespacing individual members.
Namespacing vs. objects

One could argue that this makes the structure of the document unnecessarily complex

I disagree and strongly prefer
"revision": {
  "tag": "5",
  "last-modified": "2015-11-14T09:08:15.000Z"
}

over
"revision:tag": "5",
"revision:last-modified": "2015-11-14T09:08:15.000Z"

Particularly if the dynamic keys idea gains traction, the colon-separated namespace syntax could be ditched entirely.

Responses...
@ziege and @bintoro

I think the client should send a list of extensions it wants to use instead [of all those it supports].

That sounds good! I think that, at the beginning, before we have an OPTIONS request for discovering available extensions programmatically and before clients support a ton of extensions, many clients will want to list all the extensions they support. But I agree that there's no reason to require that they do so, and that making that a requirement would be problematic long term.

The current error response for missing extensions doesn't allow to list multiple missing extensions (in one error object). Why not combine the error with the proposed OPTIONS response?

I definitely like the key names in your proposal better (e.g. "href"), and some version of it could work. But it also seems to be adding more new concepts than necessary. To support multiple missing extensions, wouldn't it be easier to just return multiple error objects (all with "status": "415") than to try to combine them?

Your questions regarding versioning

I tried to follow your comments, but I think I'm a bit confused or that we're talking past each other. Maybe it'd be best to discuss this by phone or in a Google Hangout or something?

What about my key map suggestion, which would solve this problem in a more general and compatible way?

The issue is that it requires the consumer to pre-process the document in order to make it valid JSON-LD, which means that all the existing JSON-LD software can't work out of the box. That's a huge downside in my opinion. By contrast, if the same document is both valid JSON API and JSON LD, it can be served like this:
Content-Type: application/vnd.api+json
Link: <http://example.com/context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"

And then it'll be readable by both sets of tools.

I'm still not a fan of implicit namespaces.

That's fine! My new proposal is below after my responses to @bintoro.

Wow. Kudos to both @ethanresnick and @ziege for this work.

Thanks! And thanks to @ziege too!

Names may be cheap, but good names are not... valuable generic keys [should] end up with those extensions that at least have a decent probability of becoming the de facto standards for each application.

Yup... and "implicit namespacing" was designed to solve this by encouraging multiple similar extensions to claim the same key, so as not to eat too much into the global space. But it seems like that wasn't clear, which is just more evidence that my proposal was too complicated/trying to be too clever for it's own good. A new proposal is below.

We could require that only objects, not individual members, may be embedded like this. So, "valid:title-case": true would have to be expressed as "valid": {"title-case": true}.

Personally, assuming we made the object form unambiguous by properly assigning the key names, I think I'd still be in favor of a version that has some sort of distinct character (like a colon) when one extension is used within another. I just think that makes it clearer visually that composition is going on and makes such composition more of a special case (which I think it should be).
New Namespacing Proposal
Design Goals
My design goals were to:

Keep the power of the XML-style namespacing that @ziege's been advocating for for preventing conflicts

Find a way for extension members to look as nice as base spec members (since, as discussed earlier, we should expect that extensions that become defacto standards will continue to be implemented as extensions).

Make native JSON-LD possible (related to the above).

Find one solution that would for both extensions and "links" keys. By this I mean: the extension namespacing problem is about mapping a short name (the key/namespace prefix in the document) to a URI—the URI of the extension. But that same problem also comes up in the "links" object where, eventually, I think we'll want a short key to be mappable to a full URI so that it can serve as an RFC 5988 "external relation". Therefore, we should find one mapping solution that works for both cases (and we should learn from CURIEs as prior art).

Maintain the assumption that some naive clients will simply memorize the name of each key—rather than actually looking it up dynamically.

The Proposal
Keeping the last assumption above rules out a raw version of @bintoro's dynamic key suggestion, I think, because it would mean that the base spec could never add any keys without preventing existing APIs from using them. That is, any key the base spec added might be used by some API as an extension's "key", and were that API to change that "key", to take advantage of the new base spec feature, it would break those naive clients. (Likewise, if the "templates" extension added an "ext-valid" key, the same would happen.) But I think @bintoro's suggestion is definitely on the right track.
Here's what I propose:

First, let's special case JSON-LD. I played with a bunch of ideas for making JSON-LD part of some common design (e.g. "suffix@prefix" style "reverse CURIES", among other things) and they all added way more complexity than would just saying:

When a JSON API document is served as JSON-LD (i.e. using the Link header l mentioned earlier), the producer MAY add members to the document that begin with an "@" sign to convey JSON-LD information.

So I vote we do that.

Add the concept of a "URI Alias" which would be very similar to a CURIE. It would:

be a string that has three parts (a prefix, separator, and suffix) and that can be expanded to a full URI;

The prefix would be associated with a URI, and the expansion of the alias to a full URI would work by appending the suffix to the URI associated with the prefix

The prefix can be empty (though an empty string is not valid).

However, unlike a CURIE:

The separator between the prefix and the reference would be a / rather than a :. This prevents the ambiguity between CURIEs and absolute URIs (e.g. "mailto:xxx" could be either, but "mailto/xxx" can only be a URI alias). This allows aliases to be used in the "links" object alongside full URIs.

The prefix must contain at least one non [a-z] character (just like user-defined query parameters), which could be an "@", but otherwise it must comply with member naming rules.

The suffix and the separator can be omitted (instead of the prefix and separator being omittable, which is the rule for standard CURIEs). However, the separator and suffix can ONLY be omitted when the alias resolves to an extension's URI and isn't used inside another extension's data.

So, the following would all be valid aliases:
- last-modified (just a prefix)
- last-modified/date-time and my-rel/  (a prefix and suffix, which can be empty)
- /test and / (an empty prefix with a suffix, empty or not)

Commit to only adding base spec keys going forward that have only a-z characters in them.

Require that if a URI appears in a "links" key as an extended relation, which I think we'll allow soon, it must be an absolute URI.

Note: The prior two bullets combine to makes it easy to distinguish whether any given key comes from the base spec or an extension, or is a URI alias, or an absolute URI. Absolute URIs will be the only ones with a :; base spec keys will contain only [a-z] and won't be inside extensions' data; and the rest will be URI aliases (or, in "links" registered relation names, but those can't have a /, which the aliases must).

But the user only has to remember two rules to avoid all conflicts: 1) pick prefixes with a non-[a-z] character and 2) use a / in extended link relation aliases. To me, that seems reasonable.

Then, the user defines one central mapping between URIs and aliases prefixes, in a "prefixes" key.

If a prefix is encountered that isn't associated with a URI, the whole key/value pair that had the alias as its key must be ignored.

So, the following is an example document with the different possibilities:
{
  "prefixes": {
    "last-modified": "http://jsonapi.org/ext/last-modified",
    "request-template": "http://jsonapi.org/ext/templating",
    "common-validators": "http://jsonapi.org/ext/validators",
    "API": "http://example.com/rels/"
  },
  "links": {
    "profile": ["http://jsonapi.org/ext/last-modified", "http://jsonapi.org/ext/templating", "http://jsonapi.org/ext/validators"],

    // a custom link relation.
    // Clearly identifiable as an absolute URI, not an alias, because it has a colon.
    "http://example.com/rels/entry-point": "http://example.com/api",

    // The same link relation as an alias.
    // Clearly differentiable from a standard relation because it has a slash.
    "API/entry-point": "http://example.com/api",

    // And another relation using the same prefix
    "API/publish": {
       "href": "http://example.com/api/articles/1",

       // from the profile extension referenced earlier!
       // Because this key can't be from the base spec (it's not alphanumeric)
       // so it's a URI alias, and it resolves to the extension's URI. Note that the user
       // has full control over this key name, as long as it has some non [a-z] key.
       "request-template": {
          // everything in here is assumed by default to belong to the extension;
          // another extension can only come in using an alias with a  `/`,
          // like with the validation below.
          "method": "POST",
          "fields": [{
            "name":  "action",
            "value": "publish"
          }, {
            "name": "auth-token",