aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Alexander <neilalexander@users.noreply.github.com>2020-02-05 18:06:39 +0000
committerGitHub <noreply@github.com>2020-02-05 18:06:39 +0000
commitc20109a57357fbb2cd0857485e2cca4a58c37d1b (patch)
tree3b7105fc05fea78dbeece6960e42517419d893d5
parent880d8ae0246c8b123fdc827d2c03b4cb1b6920bc (diff)
Implement room version capabilities in CS API (#866)
* Add wiring for querying the roomserver for the default room version * Try to implement /capabilities for room versions * Update copyright notices * Update sytests, add /capabilities endpoint into CS API * Update sytest-whitelist * Add GetDefaultRoomVersion * Fix cases where state package was shadowed * Fix version formatting * Update Dockerfile to Go 1.13.6 * oh yes types I remember * And fix the default too
-rw-r--r--clientapi/routing/capabilities.go51
-rw-r--r--clientapi/routing/routing.go6
-rw-r--r--docker/Dockerfile2
-rw-r--r--roomserver/api/query.go34
-rw-r--r--roomserver/input/events.go6
-rw-r--r--roomserver/input/latest_events.go10
-rw-r--r--roomserver/query/query.go57
-rw-r--r--roomserver/state/database/database.go16
-rw-r--r--roomserver/state/state.go16
-rw-r--r--roomserver/state/v1/state_test.go2
-rw-r--r--roomserver/storage/storage.go1
-rw-r--r--roomserver/version/version.go18
-rw-r--r--sytest-blacklist3
-rw-r--r--sytest-whitelist5
14 files changed, 208 insertions, 19 deletions
diff --git a/clientapi/routing/capabilities.go b/clientapi/routing/capabilities.go
new file mode 100644
index 00000000..c8743386
--- /dev/null
+++ b/clientapi/routing/capabilities.go
@@ -0,0 +1,51 @@
+// Copyright 2020 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package routing
+
+import (
+ "net/http"
+
+ "github.com/matrix-org/dendrite/clientapi/httputil"
+ roomserverAPI "github.com/matrix-org/dendrite/roomserver/api"
+
+ "github.com/matrix-org/util"
+)
+
+// SendMembership implements PUT /rooms/{roomID}/(join|kick|ban|unban|leave|invite)
+// by building a m.room.member event then sending it to the room server
+func GetCapabilities(
+ req *http.Request, queryAPI roomserverAPI.RoomserverQueryAPI,
+) util.JSONResponse {
+ roomVersionsQueryReq := roomserverAPI.QueryRoomVersionCapabilitiesRequest{}
+ var roomVersionsQueryRes roomserverAPI.QueryRoomVersionCapabilitiesResponse
+ if err := queryAPI.QueryRoomVersionCapabilities(
+ req.Context(),
+ &roomVersionsQueryReq,
+ &roomVersionsQueryRes,
+ ); err != nil {
+ return httputil.LogThenError(req, err)
+ }
+
+ response := map[string]interface{}{
+ "capabilities": map[string]interface{}{
+ "m.room_versions": roomVersionsQueryRes,
+ },
+ }
+
+ return util.JSONResponse{
+ Code: http.StatusOK,
+ JSON: response,
+ }
+}
diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go
index 5c98cd0d..bd326900 100644
--- a/clientapi/routing/routing.go
+++ b/clientapi/routing/routing.go
@@ -551,4 +551,10 @@ func Setup(
return DeleteTag(req, accountDB, device, vars["userId"], vars["roomId"], vars["tag"], syncProducer)
}),
).Methods(http.MethodDelete, http.MethodOptions)
+
+ r0mux.Handle("/capabilities",
+ common.MakeAuthAPI("capabilities", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse {
+ return GetCapabilities(req, queryAPI)
+ }),
+ ).Methods(http.MethodGet)
}
diff --git a/docker/Dockerfile b/docker/Dockerfile
index d8f9de42..c88b7761 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM docker.io/golang:1.12.5-alpine3.9
+FROM docker.io/golang:1.13.6-alpine
RUN mkdir /build
diff --git a/roomserver/api/query.go b/roomserver/api/query.go
index b3fa0184..e1850e72 100644
--- a/roomserver/api/query.go
+++ b/roomserver/api/query.go
@@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -244,6 +246,15 @@ type QueryServersInRoomAtEventResponse struct {
Servers []gomatrixserverlib.ServerName `json:"servers"`
}
+// QueryRoomVersionCapabilities asks for the default room version
+type QueryRoomVersionCapabilitiesRequest struct{}
+
+// QueryRoomVersionCapabilitiesResponse is a response to QueryServersInRoomAtEventResponse
+type QueryRoomVersionCapabilitiesResponse struct {
+ DefaultRoomVersion string `json:"default"`
+ AvailableRoomVersions map[string]string `json:"available"`
+}
+
// RoomserverQueryAPI is used to query information from the room server.
type RoomserverQueryAPI interface {
// Query the latest events and state for a room from the room server.
@@ -323,6 +334,13 @@ type RoomserverQueryAPI interface {
request *QueryServersInRoomAtEventRequest,
response *QueryServersInRoomAtEventResponse,
) error
+
+ // Asks for the default room version as preferred by the server.
+ QueryRoomVersionCapabilities(
+ ctx context.Context,
+ request *QueryRoomVersionCapabilitiesRequest,
+ response *QueryRoomVersionCapabilitiesResponse,
+ ) error
}
// RoomserverQueryLatestEventsAndStatePath is the HTTP path for the QueryLatestEventsAndState API.
@@ -358,6 +376,9 @@ const RoomserverQueryBackfillPath = "/api/roomserver/queryBackfill"
// RoomserverQueryServersInRoomAtEventPath is the HTTP path for the QueryServersInRoomAtEvent API
const RoomserverQueryServersInRoomAtEventPath = "/api/roomserver/queryServersInRoomAtEvents"
+// RoomserverQueryRoomVersionCapabilitiesPath is the HTTP path for the QueryRoomVersionCapabilities API
+const RoomserverQueryRoomVersionCapabilitiesPath = "/api/roomserver/queryRoomVersionCapabilities"
+
// NewRoomserverQueryAPIHTTP creates a RoomserverQueryAPI implemented by talking to a HTTP POST API.
// If httpClient is nil then it uses the http.DefaultClient
func NewRoomserverQueryAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverQueryAPI {
@@ -514,3 +535,16 @@ func (h *httpRoomserverQueryAPI) QueryServersInRoomAtEvent(
apiURL := h.roomserverURL + RoomserverQueryServersInRoomAtEventPath
return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
}
+
+// QueryServersInRoomAtEvent implements RoomServerQueryAPI
+func (h *httpRoomserverQueryAPI) QueryRoomVersionCapabilities(
+ ctx context.Context,
+ request *QueryRoomVersionCapabilitiesRequest,
+ response *QueryRoomVersionCapabilitiesResponse,
+) error {
+ span, ctx := opentracing.StartSpanFromContext(ctx, "QueryRoomVersionCapabilities")
+ defer span.Finish()
+
+ apiURL := h.roomserverURL + RoomserverQueryRoomVersionCapabilitiesPath
+ return commonHTTP.PostJSON(ctx, span, h.httpClient, apiURL, request, response)
+}
diff --git a/roomserver/input/events.go b/roomserver/input/events.go
index 10ccb648..03023a4a 100644
--- a/roomserver/input/events.go
+++ b/roomserver/input/events.go
@@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -151,7 +153,7 @@ func calculateAndSetState(
event gomatrixserverlib.Event,
) error {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, db)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, db)
if err != nil {
return err
}
@@ -169,7 +171,7 @@ func calculateAndSetState(
}
} else {
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
- if stateAtEvent.BeforeStateSnapshotNID, err = state.CalculateAndStoreStateBeforeEvent(ctx, event, roomNID); err != nil {
+ if stateAtEvent.BeforeStateSnapshotNID, err = roomState.CalculateAndStoreStateBeforeEvent(ctx, event, roomNID); err != nil {
return err
}
}
diff --git a/roomserver/input/latest_events.go b/roomserver/input/latest_events.go
index 760677db..7e03d544 100644
--- a/roomserver/input/latest_events.go
+++ b/roomserver/input/latest_events.go
@@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -172,7 +174,7 @@ func (u *latestEventsUpdater) doUpdateLatestEvents() error {
func (u *latestEventsUpdater) latestState() error {
var err error
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, u.db)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, u.db)
if err != nil {
return err
}
@@ -181,21 +183,21 @@ func (u *latestEventsUpdater) latestState() error {
for i := range u.latest {
latestStateAtEvents[i] = u.latest[i].StateAtEvent
}
- u.newStateNID, err = state.CalculateAndStoreStateAfterEvents(
+ u.newStateNID, err = roomState.CalculateAndStoreStateAfterEvents(
u.ctx, u.roomNID, latestStateAtEvents,
)
if err != nil {
return err
}
- u.removed, u.added, err = state.DifferenceBetweeenStateSnapshots(
+ u.removed, u.added, err = roomState.DifferenceBetweeenStateSnapshots(
u.ctx, u.oldStateNID, u.newStateNID,
)
if err != nil {
return err
}
- u.stateBeforeEventRemoves, u.stateBeforeEventAdds, err = state.DifferenceBetweeenStateSnapshots(
+ u.stateBeforeEventRemoves, u.stateBeforeEventAdds, err = roomState.DifferenceBetweeenStateSnapshots(
u.ctx, u.newStateNID, u.stateAtEvent.BeforeStateSnapshotNID,
)
return err
diff --git a/roomserver/query/query.go b/roomserver/query/query.go
index d318fc00..f138686b 100644
--- a/roomserver/query/query.go
+++ b/roomserver/query/query.go
@@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -18,6 +20,7 @@ import (
"context"
"encoding/json"
"net/http"
+ "strconv"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/dendrite/roomserver/api"
@@ -25,6 +28,7 @@ import (
"github.com/matrix-org/dendrite/roomserver/state"
"github.com/matrix-org/dendrite/roomserver/state/database"
"github.com/matrix-org/dendrite/roomserver/types"
+ "github.com/matrix-org/dendrite/roomserver/version"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
)
@@ -100,7 +104,7 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
response *api.QueryLatestEventsAndStateResponse,
) error {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil {
return err
}
@@ -121,7 +125,7 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState(
}
// Look up the currrent state for the requested tuples.
- stateEntries, err := state.LoadStateAtSnapshotForStringTuples(
+ stateEntries, err := roomState.LoadStateAtSnapshotForStringTuples(
ctx, currentStateSnapshotNID, request.StateToFetch,
)
if err != nil {
@@ -144,7 +148,7 @@ func (r *RoomserverQueryAPI) QueryStateAfterEvents(
response *api.QueryStateAfterEventsResponse,
) error {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil {
return err
}
@@ -170,7 +174,7 @@ func (r *RoomserverQueryAPI) QueryStateAfterEvents(
response.PrevEventsExist = true
// Look up the currrent state for the requested tuples.
- stateEntries, err := state.LoadStateAfterEventsForStringTuples(
+ stateEntries, err := roomState.LoadStateAfterEventsForStringTuples(
ctx, prevStates, request.StateToFetch,
)
if err != nil {
@@ -327,7 +331,7 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
ctx context.Context, eventNID types.EventNID, joinedOnly bool,
) ([]types.Event, error) {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil {
return []types.Event{}, err
}
@@ -345,7 +349,7 @@ func (r *RoomserverQueryAPI) getMembershipsBeforeEventNID(
}
// Fetch the state as it was when this event was fired
- stateEntries, err := state.LoadCombinedStateAfterEvents(ctx, prevState)
+ stateEntries, err := roomState.LoadCombinedStateAfterEvents(ctx, prevState)
if err != nil {
return nil, err
}
@@ -433,12 +437,12 @@ func (r *RoomserverQueryAPI) checkServerAllowedToSeeEvent(
ctx context.Context, eventID string, serverName gomatrixserverlib.ServerName,
) (bool, error) {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil {
return false, err
}
- stateEntries, err := state.LoadStateAtEvent(ctx, eventID)
+ stateEntries, err := roomState.LoadStateAtEvent(ctx, eventID)
if err != nil {
return false, err
}
@@ -593,7 +597,7 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
response *api.QueryStateAndAuthChainResponse,
) error {
// TODO: get the correct room version
- state, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
+ roomState, err := state.GetStateResolutionAlgorithm(state.StateResolutionAlgorithmV1, r.DB)
if err != nil {
return err
}
@@ -620,7 +624,7 @@ func (r *RoomserverQueryAPI) QueryStateAndAuthChain(
response.PrevEventsExist = true
// Look up the currrent state for the requested tuples.
- stateEntries, err := state.LoadCombinedStateAfterEvents(
+ stateEntries, err := roomState.LoadCombinedStateAfterEvents(
ctx, prevStates,
)
if err != nil {
@@ -723,6 +727,25 @@ func (r *RoomserverQueryAPI) QueryServersInRoomAtEvent(
return nil
}
+// QueryRoomVersionCapabilities implements api.RoomserverQueryAPI
+func (r *RoomserverQueryAPI) QueryRoomVersionCapabilities(
+ ctx context.Context,
+ request *api.QueryRoomVersionCapabilitiesRequest,
+ response *api.QueryRoomVersionCapabilitiesResponse,
+) error {
+ response.DefaultRoomVersion = strconv.Itoa(int(version.GetDefaultRoomVersion()))
+ response.AvailableRoomVersions = make(map[string]string)
+ for v, desc := range version.GetSupportedRoomVersions() {
+ sv := strconv.Itoa(int(v))
+ if desc.Stable {
+ response.AvailableRoomVersions[sv] = "stable"
+ } else {
+ response.AvailableRoomVersions[sv] = "unstable"
+ }
+ }
+ return nil
+}
+
// SetupHTTP adds the RoomserverQueryAPI handlers to the http.ServeMux.
// nolint: gocyclo
func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) {
@@ -880,4 +903,18 @@ func (r *RoomserverQueryAPI) SetupHTTP(servMux *http.ServeMux) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response}
}),
)
+ servMux.Handle(
+ api.RoomserverQueryRoomVersionCapabilitiesPath,
+ common.MakeInternalAPI("QueryRoomVersionCapabilities", func(req *http.Request) util.JSONResponse {
+ var request api.QueryRoomVersionCapabilitiesRequest
+ var response api.QueryRoomVersionCapabilitiesResponse
+ if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
+ return util.ErrorResponse(err)
+ }
+ if err := r.QueryRoomVersionCapabilities(req.Context(), &request, &response); err != nil {
+ return util.ErrorResponse(err)
+ }
+ return util.JSONResponse{Code: http.StatusOK, JSON: &response}
+ }),
+ )
}
diff --git a/roomserver/state/database/database.go b/roomserver/state/database/database.go
index 546f06e8..ede6c5ec 100644
--- a/roomserver/state/database/database.go
+++ b/roomserver/state/database/database.go
@@ -1,3 +1,19 @@
+// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
package database
import (
diff --git a/roomserver/state/state.go b/roomserver/state/state.go
index bbc27ad8..687a120e 100644
--- a/roomserver/state/state.go
+++ b/roomserver/state/state.go
@@ -1,3 +1,19 @@
+// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
package state
import (
diff --git a/roomserver/state/v1/state_test.go b/roomserver/state/v1/state_test.go
index 414ca2a1..4dc7e52e 100644
--- a/roomserver/state/v1/state_test.go
+++ b/roomserver/state/v1/state_test.go
@@ -1,4 +1,6 @@
// Copyright 2017 Vector Creations Ltd
+// Copyright 2018 New Vector Ltd
+// Copyright 2019-2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/roomserver/storage/storage.go b/roomserver/storage/storage.go
index 67efe656..90841168 100644
--- a/roomserver/storage/storage.go
+++ b/roomserver/storage/storage.go
@@ -55,7 +55,6 @@ type Database interface {
GetMembershipEventNIDsForRoom(ctx context.Context, roomNID types.RoomNID, joinOnly bool) ([]types.EventNID, error)
EventsFromIDs(ctx context.Context, eventIDs []string) ([]types.Event, error)
GetRoomVersionForRoom(ctx context.Context, roomNID types.RoomNID) (int64, error)
- //GetRoomVersionForEvent(ctx context.Context, eventNID types.EventNID) int64
}
// NewPublicRoomsServerDatabase opens a database connection.
diff --git a/roomserver/version/version.go b/roomserver/version/version.go
index 74d04d9b..0943e384 100644
--- a/roomserver/version/version.go
+++ b/roomserver/version/version.go
@@ -1,3 +1,17 @@
+// Copyright 2020 The Matrix.org Foundation C.I.C.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
package version
import (
@@ -69,6 +83,10 @@ var roomVersions = map[RoomVersionID]RoomVersionDescription{
},
}
+func GetDefaultRoomVersion() RoomVersionID {
+ return RoomVersionV1
+}
+
func GetRoomVersions() map[RoomVersionID]RoomVersionDescription {
return roomVersions
}
diff --git a/sytest-blacklist b/sytest-blacklist
index 4c36e477..5ddc18f6 100644
--- a/sytest-blacklist
+++ b/sytest-blacklist
@@ -19,3 +19,6 @@ Alias creators can delete alias with no ops
# Blacklisted because matrix-org/dendrite#847 might have broken it but we're not
# really sure and we need it pretty badly anyway
Real non-joined users can get individual state for world_readable rooms after leaving
+
+# Blacklisted until matrix-org/dendrite#862 is reverted due to Riot bug
+Latest account data appears in v2 /sync
diff --git a/sytest-whitelist b/sytest-whitelist
index 0600e2e5..efc97b5e 100644
--- a/sytest-whitelist
+++ b/sytest-whitelist
@@ -112,7 +112,7 @@ User can invite local user to room with version 4
Should reject keys claiming to belong to a different user
Can add account data
Can add account data to room
-Latest account data appears in v2 /sync
+#Latest account data appears in v2 /sync
New account data appears in incremental v2 /sync
Checking local federation server
Inbound federation can query profile data
@@ -227,3 +227,6 @@ Guest users can sync from world_readable guest_access rooms if joined
Guest users can sync from default guest_access rooms if joined
Real non-joined users cannot room initalSync for non-world_readable rooms
Push rules come down in an initial /sync
+Regular users can add and delete aliases in the default room configuration
+Regular users can add and delete aliases when m.room.aliases is restricted
+GET /r0/capabilities is not public