From 2c28bdacb8f3d79eed4c939f4af7427558d4484b Mon Sep 17 00:00:00 2001 From: cpoder Date: Thu, 15 Dec 2022 19:00:36 +0100 Subject: [PATCH] finalized upgrade to 10.14 --- .github/workflows/main.yml | 4 +- .../lora/ns/actility/ActilityConnector.java | 53 +- .../lora/ns/generic/GenericConnector.java | 37 +- .../lora/ns/kerlink/KerlinkConnector.java | 65 +- .../ns/liveobjects/LiveObjectsConnector.java | 93 +- .../java/lora/ns/loriot/LoriotConnector.java | 33 +- lora-ns-ms/pom.xml | 4 +- .../ns/connector/LNSAbstractConnector.java | 47 +- .../java/lora/ns/connector/LNSConnector.java | 23 +- .../ns/integration/LNSIntegrationService.java | 78 +- .../lora/ns/objenious/ObjeniousConnector.java | 142 +- .../lora/ns/orbiwise/OrbiwiseConnector.java | 20 +- lora-ns-ttn/pom.xml | 20 +- lora-ns-ttn/readme.md | 16 +- lora-ns-ttn/setenv.sh | 4 - lora-ns-ttn/src/main/java/lora/App.java | 6 +- .../main/java/lora/ns/ttn/TTNConnector.java | 148 +- .../protoc-gen-go-flags/annotations.proto | 103 + .../protoc-gen-go-json/annotations.proto | 105 + .../validate/validate.proto | 863 + .../gogo/protobuf/gogoproto/gogo.proto | 144 + .../main/proto/google/api/annotations.proto | 31 + .../src/main/proto/google/api/http.proto | 375 + .../main/proto/lorawan-stack/api/_api.proto | 5 + .../src/main/proto/lorawan-stack/api/api.md | 4218 +++- .../proto/lorawan-stack/api/api.swagger.json | 19815 ++++++++++++---- .../proto/lorawan-stack/api/application.proto | 158 +- .../api/application_services.proto | 36 + .../lorawan-stack/api/applicationserver.proto | 160 +- ...plicationserver_integrations_storage.proto | 136 + .../api/applicationserver_packages.proto | 62 +- .../api/applicationserver_pubsub.proto | 133 +- .../api/applicationserver_web.proto | 118 +- .../main/proto/lorawan-stack/api/client.proto | 111 +- .../lorawan-stack/api/client_services.proto | 27 +- .../proto/lorawan-stack/api/cluster.proto | 7 +- .../api/configuration_services.proto | 144 +- .../lorawan-stack/api/contact_info.proto | 34 +- .../api/deviceclaimingserver.proto | 251 +- .../lorawan-stack/api/devicerepository.proto | 481 + .../lorawan-stack/api/email_messages.proto | 28 + .../proto/lorawan-stack/api/end_device.proto | 566 +- .../api/end_device_services.proto | 31 +- .../main/proto/lorawan-stack/api/enums.proto | 9 +- .../main/proto/lorawan-stack/api/error.proto | 12 +- .../main/proto/lorawan-stack/api/events.proto | 52 +- .../proto/lorawan-stack/api/gateway.proto | 295 +- .../api/gateway_configuration.proto | 47 + .../lorawan-stack/api/gateway_services.proto | 31 +- .../lorawan-stack/api/gatewayserver.proto | 54 +- .../proto/lorawan-stack/api/identifiers.proto | 166 +- .../lorawan-stack/api/identityserver.proto | 34 +- .../main/proto/lorawan-stack/api/join.proto | 46 +- .../proto/lorawan-stack/api/joinserver.proto | 290 +- .../main/proto/lorawan-stack/api/keys.proto | 40 +- .../proto/lorawan-stack/api/lorawan.proto | 481 +- .../lorawan-stack/api/message_services.proto | 49 - .../proto/lorawan-stack/api/messages.proto | 254 +- .../proto/lorawan-stack/api/metadata.proto | 94 +- .../main/proto/lorawan-stack/api/mqtt.proto | 5 +- .../lorawan-stack/api/networkserver.proto | 94 +- .../api/notification_service.proto | 208 + .../main/proto/lorawan-stack/api/oauth.proto | 51 +- .../lorawan-stack/api/oauth_services.proto | 10 + .../lorawan-stack/api/organization.proto | 113 +- .../api/organization_services.proto | 27 + .../lorawan-stack/api/packetbrokeragent.proto | 445 +- .../proto/lorawan-stack/api/picture.proto | 8 +- .../lorawan-stack/api/qrcodegenerator.proto | 55 +- .../proto/lorawan-stack/api/regional.proto | 15 +- .../main/proto/lorawan-stack/api/rights.proto | 40 +- .../lorawan-stack/api/search_services.proto | 341 +- .../proto/lorawan-stack/api/secrets.proto | 40 + .../main/proto/lorawan-stack/api/user.proto | 172 +- .../lorawan-stack/api/user_services.proto | 36 + .../options/annotations.proto | 44 + .../options/openapiv2.proto | 659 + .../src/main/resources/application.properties | 2 +- .../devicemanagement-lora/app.module.ts | 253 +- .../extra-webpack.config.js | 2 +- .../devicemanagement-lora/package-lock.json | 34 +- .../devicemanagement-lora/package.json | 5 +- 82 files changed, 27487 insertions(+), 6061 deletions(-) delete mode 100644 lora-ns-ttn/setenv.sh create mode 100644 lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto create mode 100644 lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto create mode 100644 lora-ns-ttn/src/main/proto/github.com/envoyproxy/protoc-gen-validate/validate/validate.proto create mode 100644 lora-ns-ttn/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto create mode 100644 lora-ns-ttn/src/main/proto/google/api/annotations.proto create mode 100644 lora-ns-ttn/src/main/proto/google/api/http.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_integrations_storage.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/devicerepository.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/email_messages.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_configuration.proto delete mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/message_services.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/notification_service.proto create mode 100644 lora-ns-ttn/src/main/proto/lorawan-stack/api/secrets.proto create mode 100644 lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/annotations.proto create mode 100644 lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/openapiv2.proto diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd0e9a2e0..12762905e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ jobs: done - name: Build webapp run: | - cd web/1011.0.12/devicemanagement-lora + cd web/1014.0.172/devicemanagement-lora npm install --force npm run build cd dist/apps/devicemanagement-lora @@ -80,7 +80,7 @@ jobs: owner, repo, release_id: release.data.id, name: 'devicemanagement-lora.zip', - data: await fs.readFile('web/1011.0.12/devicemanagement-lora/dist/apps/devicemanagement-lora/devicemanagement-lora.zip') + data: await fs.readFile('web/1014.0.172/devicemanagement-lora/dist/apps/devicemanagement-lora/devicemanagement-lora.zip') }); for (let dir of await fs.readdir('.', {withFileTypes: true})) { diff --git a/lora-ns-actility/src/main/java/lora/ns/actility/ActilityConnector.java b/lora-ns-actility/src/main/java/lora/ns/actility/ActilityConnector.java index c023d989a..86885a328 100644 --- a/lora-ns-actility/src/main/java/lora/ns/actility/ActilityConnector.java +++ b/lora-ns-actility/src/main/java/lora/ns/actility/ActilityConnector.java @@ -5,13 +5,14 @@ import java.util.ArrayList; import java.util.Base64; import java.util.List; -import java.util.Optional; import java.util.Properties; import java.util.Random; -import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; - import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Autowired; + +import com.cumulocity.microservice.subscription.service.MicroserviceSubscriptionsService; +import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; import c8y.ConnectionState; import lombok.extern.slf4j.Slf4j; @@ -115,7 +116,8 @@ protected void init() { @Override public LNSResponse> getDevices() { - LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList()); + LNSResponse> result = new LNSResponse>().withOk(true) + .withResult(new ArrayList()); return result; } @@ -151,7 +153,8 @@ public LNSResponse sendDownlink(DownlinkData operation) { securityParams.setAsId("cumulocity"); securityParams.setAsKey(downlinkAsKey); message.setSecurityParams(securityParams); - Response response = actilityCoreService.sendDownlink(operation.getDevEui(), message).execute(); + Response response = actilityCoreService.sendDownlink(operation.getDevEui(), message) + .execute(); log.info("Response from Thingpark was {}", response.code()); result.withOk(true).withResult(String.valueOf(downlinkCounter)); } catch (Exception e) { @@ -191,8 +194,8 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { - LNSResponse> result = new LNSResponse>().withOk(true); + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { + LNSResponse result = new LNSResponse().withOk(true); log.info("Configuring routings to: {} with credentials: {}:{}", url, login, password); String connectionId = null; @@ -268,8 +271,11 @@ public LNSResponse> configureRoutings(String url, String tenant, St return result; } + @Autowired + private MicroserviceSubscriptionsService subscriptionsService; + @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { + public LNSResponse removeRoutings() { LNSResponse result = new LNSResponse().withOk(true); try { Response> response = actilityCoreService.getDevices().execute(); @@ -294,7 +300,7 @@ public LNSResponse removeRoutings(String tenant, List routeIds) { if (response.isSuccessful()) { response.body().forEach(connection -> { log.info("Found Connection {}", connection.getName()); - if (connection.getName().equals(tenant + "-" + this.getId())) { + if (connection.getName().equals(subscriptionsService.getTenant() + "-" + this.getId())) { try { actilityCoreService.deleteConnection(connection.getId()).execute(); } catch (Exception e) { @@ -335,7 +341,7 @@ public LNSResponse> getGateways() { try { Response> response = actilityCoreService.getBaseStations().execute(); if (response.isSuccessful()) { - for (BaseStation baseStation: response.body()) { + for (BaseStation baseStation : response.body()) { Response r = actilityCoreService.getBaseStation(baseStation.getRef()).execute(); if (r.isSuccessful()) { baseStation = r.body(); @@ -353,22 +359,29 @@ public LNSResponse> getGateways() { } if (baseStation.getStatistics() != null) { C8YData data = new C8YData(); - if (baseStation.getStatistics().getConnectionState() == ConnectionStateEnum.CNX && baseStation.getStatistics().getHealthState() == HealthStateEnum.ACTIVE) { + if (baseStation.getStatistics().getConnectionState() == ConnectionStateEnum.CNX + && baseStation.getStatistics().getHealthState() == HealthStateEnum.ACTIVE) { g.setStatus(ConnectionState.AVAILABLE); } else { g.setStatus(ConnectionState.UNAVAILABLE); } if (baseStation.getStatistics().getTemperature() != null) { - data.addMeasurement(null, "Temperature", "T", "°C", BigDecimal.valueOf(baseStation.getStatistics().getTemperature()), new DateTime()); + data.addMeasurement(null, "Temperature", "T", "°C", + BigDecimal.valueOf(baseStation.getStatistics().getTemperature()), + new DateTime()); } if (baseStation.getStatistics().getCpUUsage() != null) { - data.addMeasurement(null, "CPU", "Usage", "%", BigDecimal.valueOf(baseStation.getStatistics().getCpUUsage()), new DateTime()); + data.addMeasurement(null, "CPU", "Usage", "%", + BigDecimal.valueOf(baseStation.getStatistics().getCpUUsage()), new DateTime()); } if (baseStation.getStatistics().getRaMUsage() != null) { - data.addMeasurement(null, "RAM", "Usage", "%", BigDecimal.valueOf(baseStation.getStatistics().getRaMUsage()), new DateTime()); + data.addMeasurement(null, "RAM", "Usage", "%", + BigDecimal.valueOf(baseStation.getStatistics().getRaMUsage()), new DateTime()); } if (baseStation.getStatistics().getBatteryLevel() != null) { - data.addMeasurement(null, "Battery", "level", "%", BigDecimal.valueOf(baseStation.getStatistics().getBatteryLevel()), new DateTime()); + data.addMeasurement(null, "Battery", "level", "%", + BigDecimal.valueOf(baseStation.getStatistics().getBatteryLevel()), + new DateTime()); } g.setData(data); } @@ -416,7 +429,8 @@ public LNSResponse provisionGateway(GatewayProvisioning gatewayProvisionin if (gatewayProvisioning.getLng() != null) { baseStation.setGeoLongitude(gatewayProvisioning.getLng().floatValue()); } - baseStation.setBaseStationProfileId(gatewayProvisioning.getAdditionalProperties().getProperty("gatewayProfile")); + baseStation.setBaseStationProfileId( + gatewayProvisioning.getAdditionalProperties().getProperty("gatewayProfile")); baseStation.setRfRegionId(gatewayProvisioning.getAdditionalProperties().getProperty("rfRegion")); Response response = actilityCoreService.createBaseStation(baseStation).execute(); if (!response.isSuccessful()) { @@ -453,7 +467,8 @@ public List getBaseStationProfiles() { if (response.isSuccessful()) { result = response.body(); } else { - log.error("Error while retrieving the list of base station profiles: {}", response.errorBody().string()); + log.error("Error while retrieving the list of base station profiles: {}", + response.errorBody().string()); } } catch (Exception e) { e.printStackTrace(); @@ -475,4 +490,8 @@ public List getRFRegions() { } return result; } + + public boolean hasGatewayManagementCapability() { + return true; + } } diff --git a/lora-ns-generic/src/main/java/lora/ns/generic/GenericConnector.java b/lora-ns-generic/src/main/java/lora/ns/generic/GenericConnector.java index 1e231ae7c..7d8327ea0 100644 --- a/lora-ns-generic/src/main/java/lora/ns/generic/GenericConnector.java +++ b/lora-ns-generic/src/main/java/lora/ns/generic/GenericConnector.java @@ -24,12 +24,13 @@ public GenericConnector(Properties properties) { public GenericConnector(ManagedObjectRepresentation instance) { super(instance); } - @Override + + @Override protected void init() { - // Configure LNS API access here - } + // Configure LNS API access here + } - @Override + @Override public LNSResponse> getDevices() { return new LNSResponse>().withOk(false).withMessage("Not implemented."); } @@ -37,7 +38,7 @@ public LNSResponse> getDevices() { @Override public LNSResponse getDevice(String devEui) { return new LNSResponse().withOk(false).withMessage("Not implemented."); - } + } @Override public LNSResponse sendDownlink(DownlinkData operation) { @@ -46,36 +47,40 @@ public LNSResponse sendDownlink(DownlinkData operation) { @Override public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { - return new LNSResponse>().withOk(false).withMessage("Not implemented."); + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + public LNSResponse removeRoutings() { + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override public LNSResponse deprovisionDevice(String deveui) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override public LNSResponse> getGateways() { - return new LNSResponse>().withOk(false).withMessage("Not implemented."); + return new LNSResponse>().withOk(false).withMessage("Not implemented."); } - @Override + @Override public LNSResponse provisionGateway(GatewayProvisioning gatewayProvisioning) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } - @Override + @Override public LNSResponse deprovisionGateway(String id) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); + } + + public boolean hasGatewayManagementCapability() { + return false; } } diff --git a/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java b/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java index 0b65b314b..113de5308 100644 --- a/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java +++ b/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java @@ -103,7 +103,8 @@ private void login() { } public LNSResponse> getDevices() { - LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList<>()); + LNSResponse> result = new LNSResponse>().withOk(true) + .withResult(new ArrayList<>()); if (jwt == null || jwt.isExpired()) { login(); } @@ -111,16 +112,18 @@ public LNSResponse> getDevices() { headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); try { - ResponseEntity> response = restTemplate.exchange(baseUrl + "/endDevices", HttpMethod.GET, + ResponseEntity> response = restTemplate.exchange(baseUrl + "/endDevices", + HttpMethod.GET, new HttpEntity("", headers), new ParameterizedTypeReference>() { }); if (response.hasBody()) { for (EndDeviceDto endDeviceDto : response.getBody().getList()) { result.getResult() - .add(new EndDevice(endDeviceDto.getDevEui(), endDeviceDto.getName(), endDeviceDto.getClassType())); + .add(new EndDevice(endDeviceDto.getDevEui(), endDeviceDto.getName(), + endDeviceDto.getClassType())); } } - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.withOk(false).withMessage(e.getMessage()); } @@ -194,12 +197,13 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) dto.setAppEui(deviceProvisioning.getAppEUI()); dto.setAppKey(deviceProvisioning.getAppKey()); try { - ResponseEntity res = restTemplate.exchange(baseUrl + "/endDevices/" + deviceProvisioning.getDevEUI(), + ResponseEntity res = restTemplate.exchange( + baseUrl + "/endDevices/" + deviceProvisioning.getDevEUI(), HttpMethod.PUT, new HttpEntity(dto, headers), String.class); if (res.getStatusCodeValue() != 201) { result.withOk(false).withMessage(res.getBody()); } - } catch(HttpClientErrorException e) { + } catch (HttpClientErrorException e) { e.printStackTrace(); logger.error(e.getResponseBodyAsString()); result.withOk(false).withMessage(e.getResponseBodyAsString()); @@ -216,8 +220,8 @@ protected void init() { } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { - LNSResponse> result = new LNSResponse>().withOk(true); + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { + LNSResponse result = new LNSResponse().withOk(true); if (jwt == null || jwt.isExpired()) { login(); } @@ -279,7 +283,7 @@ public LNSResponse> configureRoutings(String url, String tenant, St "/downlink", "/uplink", url, tenant + "/" + login, password); logger.info("Will create a new push configuration: {}", currentPushConfigurationDto.toString()); headers.setContentType(MediaType.MULTIPART_FORM_DATA); - List mediaTypes = new ArrayList(); + List mediaTypes = new ArrayList<>(); mediaTypes.add(MediaType.APPLICATION_JSON); headers.setAccept(mediaTypes); ObjectMapper mapper = new ObjectMapper(); @@ -298,11 +302,13 @@ public LNSResponse> configureRoutings(String url, String tenant, St String[] tokens = response.getHeaders().getLocation().getPath().split("/"); configId = Integer.parseInt(tokens[tokens.length - 1]); } else { - result.withOk(false).withMessage("Something was wrong while creating the push config: " + response.getBody()); + result.withOk(false) + .withMessage("Something was wrong while creating the push config: " + response.getBody()); logger.error("Something was wrong while creating the push config: {}", response.getBody()); } } if (configId != null) { + this.setProperty("configId", configId); headers = new HttpHeaders(); headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); ClusterDto cluster = restTemplate.exchange(baseUrl + "/clusters/" + clusterId, HttpMethod.GET, @@ -315,7 +321,8 @@ public LNSResponse> configureRoutings(String url, String tenant, St ResponseEntity response = restTemplate.exchange(baseUrl + "/clusters/" + clusterId, HttpMethod.PATCH, new HttpEntity(cluster, headers), String.class); if (response.getStatusCode() != HttpStatus.NO_CONTENT) { - result.withOk(false).withMessage("Something was wrong while updating the cluster: " + response.getBody()); + result.withOk(false) + .withMessage("Something was wrong while updating the cluster: " + response.getBody()); logger.error("Something was wrong while updating the cluster: {}", response.getBody()); } } @@ -323,25 +330,18 @@ public LNSResponse> configureRoutings(String url, String tenant, St } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { - LNSResponse result = new LNSResponse().withOk(true); - if (jwt == null || jwt.isExpired()) { - login(); - } - HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); - RestTemplate restTemplate = new RestTemplate(); - PaginatedDto pushConfigurationDtos = restTemplate - .exchange(baseUrl + "/pushConfigurations", HttpMethod.GET, new HttpEntity("", headers), - new ParameterizedTypeReference>() { - }) - .getBody(); - for (PushConfigurationDto pushConfigurationDto : pushConfigurationDtos.getList()) { - if (pushConfigurationDto.getName().equals(tenant + "-" + this.getId())) { - restTemplate.exchange(baseUrl + "/pushConfigurations/" + pushConfigurationDto.getId(), - HttpMethod.DELETE, new HttpEntity("", headers), String.class); + public LNSResponse removeRoutings() { + final LNSResponse result = new LNSResponse().withOk(true); + this.getProperty("configId").ifPresent(configId -> { + if (jwt == null || jwt.isExpired()) { + login(); } - } + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.exchange(baseUrl + "/pushConfigurations/" + configId, + HttpMethod.DELETE, new HttpEntity("", headers), String.class); + }); return result; } @@ -395,7 +395,8 @@ public LNSResponse> getGateways() { }).getBody(); C8YData c8yData = new C8YData(); for (GatewayDto gatewayDto : gatewaysDto.getList()) { - result.getResult().add(new Gateway(gatewayDto.getEui(), gatewayDto.getEui(), gatewayDto.getName(), gatewayDto.getLatitude(), + result.getResult().add(new Gateway(gatewayDto.getEui(), gatewayDto.getEui(), gatewayDto.getName(), + gatewayDto.getLatitude(), gatewayDto.getLongitude(), gatewayDto.getDescription(), ConnectionState.AVAILABLE, c8yData)); } return result; @@ -408,4 +409,8 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga public LNSResponse deprovisionGateway(String id) { return new LNSResponse().withOk(false).withMessage("Not implemented"); } + + public boolean hasGatewayManagementCapability() { + return false; + } } diff --git a/lora-ns-liveobjects/src/main/java/lora/ns/liveobjects/LiveObjectsConnector.java b/lora-ns-liveobjects/src/main/java/lora/ns/liveobjects/LiveObjectsConnector.java index 16dc68fc0..da6386f9a 100644 --- a/lora-ns-liveobjects/src/main/java/lora/ns/liveobjects/LiveObjectsConnector.java +++ b/lora-ns-liveobjects/src/main/java/lora/ns/liveobjects/LiveObjectsConnector.java @@ -39,12 +39,13 @@ public LiveObjectsConnector(Properties properties) { public LiveObjectsConnector(ManagedObjectRepresentation instance) { super(instance); } - @Override + + @Override protected void init() { - // Configure LNS API access here - } + // Configure LNS API access here + } - @Override + @Override public LNSResponse> getDevices() { return new LNSResponse>().withOk(false).withMessage("Not implemented."); } @@ -52,7 +53,7 @@ public LNSResponse> getDevices() { @Override public LNSResponse getDevice(String devEui) { return new LNSResponse().withOk(false).withMessage("Not implemented."); - } + } @Override public LNSResponse sendDownlink(DownlinkData operation) { @@ -61,78 +62,88 @@ public LNSResponse sendDownlink(DownlinkData operation) { @Override public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { - LNSResponse> response = new LNSResponse>().withOk(true); - String authorization = "Basic " + Base64.getEncoder().encodeToString((tenant + "/" + login + ":" + password).getBytes()); + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { + LNSResponse response = new LNSResponse().withOk(true); + String authorization = "Basic " + + Base64.getEncoder().encodeToString((tenant + "/" + login + ":" + password).getBytes()); try { Response result = service.createActionPolicy(new ActionPolicy() - .withId(null) - .withName("Cumulocity webhook for tenant " + tenant) - .withEnabled(true) - .withActions(new Actions() - .withHttpPush(new HttpPushAction() - .withWebhookUrl(url + "/uplink").withHeaders(Map.of("Authorization", authorization)))) - .withTriggers(new ActionTriggers() - .withLoraNetwork(new LoraNetworkTrigger() - .withFilter(new LoraNetworkFilter() - .withMessageTypes(List.of(MessageType.UNCONFIRMED_DATA_UP, MessageType.CONFIRMED_DATA_UP)))))).execute(); + .withId(null) + .withName("Cumulocity webhook for tenant " + tenant) + .withEnabled(true) + .withActions(new Actions() + .withHttpPush(new HttpPushAction() + .withWebhookUrl(url + "/uplink") + .withHeaders(Map.of("Authorization", authorization)))) + .withTriggers(new ActionTriggers() + .withLoraNetwork(new LoraNetworkTrigger() + .withFilter(new LoraNetworkFilter() + .withMessageTypes(List.of(MessageType.UNCONFIRMED_DATA_UP, + MessageType.CONFIRMED_DATA_UP)))))) + .execute(); if (!result.isSuccessful()) { response.setOk(false); response.setMessage(result.errorBody().string()); } else { - response.setResult(new ArrayList<>()); - response.getResult().add(result.body().getId()); + this.setProperty("uplinkRouteId", result.body().getId()); result = service.createActionPolicy(new ActionPolicy() - .withId(null) - .withName("Cumulocity webhook for tenant " + tenant) - .withEnabled(true) - .withActions(new Actions() - .withHttpPush(new HttpPushAction() - .withWebhookUrl(url + "/downlink").withHeaders(Map.of("Authorization", authorization)))) - .withTriggers(new ActionTriggers() - .withLoraNetwork(new LoraNetworkTrigger() - .withFilter(new LoraNetworkFilter() - .withMessageTypes(List.of(MessageType.CONFIRMED_DATA_DOWN, MessageType.UNCONFIRMED_DATA_DOWN)))))).execute(); + .withId(null) + .withName("Cumulocity webhook for tenant " + tenant) + .withEnabled(true) + .withActions(new Actions() + .withHttpPush(new HttpPushAction() + .withWebhookUrl(url + "/downlink") + .withHeaders(Map.of("Authorization", authorization)))) + .withTriggers(new ActionTriggers() + .withLoraNetwork(new LoraNetworkTrigger() + .withFilter(new LoraNetworkFilter() + .withMessageTypes(List.of(MessageType.CONFIRMED_DATA_DOWN, + MessageType.UNCONFIRMED_DATA_DOWN)))))) + .execute(); if (!result.isSuccessful()) { response.setOk(false); response.setMessage(result.errorBody().string()); } else { - response.getResult().add(result.body().getId()); + this.setProperty("downlinkRouteId", result.body().getId()); } } } catch (IOException e) { e.printStackTrace(); response.withOk(false).withMessage(e.getMessage()); } - return response; + return response; } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + public LNSResponse removeRoutings() { + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override public LNSResponse deprovisionDevice(String deveui) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } @Override public LNSResponse> getGateways() { - return new LNSResponse>().withOk(false).withMessage("Not implemented."); + return new LNSResponse>().withOk(false).withMessage("Not implemented."); } - @Override + @Override public LNSResponse provisionGateway(GatewayProvisioning gatewayProvisioning) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); } - @Override + @Override public LNSResponse deprovisionGateway(String id) { - return new LNSResponse().withOk(false).withMessage("Not implemented."); + return new LNSResponse().withOk(false).withMessage("Not implemented."); + } + + public boolean hasGatewayManagementCapability() { + return false; } } diff --git a/lora-ns-loriot/src/main/java/lora/ns/loriot/LoriotConnector.java b/lora-ns-loriot/src/main/java/lora/ns/loriot/LoriotConnector.java index 350849ea7..7a0bd7d07 100644 --- a/lora-ns-loriot/src/main/java/lora/ns/loriot/LoriotConnector.java +++ b/lora-ns-loriot/src/main/java/lora/ns/loriot/LoriotConnector.java @@ -121,7 +121,8 @@ protected void init() { @Override public LNSResponse> getDevices() { - LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList()); + LNSResponse> result = new LNSResponse>().withOk(true) + .withResult(new ArrayList()); try { retrofit2.Response> response = loriotService.getDevices(getProperties().getProperty("appid")) .execute(); @@ -147,7 +148,8 @@ public LNSResponse> getDevices() { public LNSResponse getDevice(String devEui) { LNSResponse result = new LNSResponse().withOk(true); try { - retrofit2.Response response = loriotService.getDevice(getProperties().getProperty("appid"), devEui).execute(); + retrofit2.Response response = loriotService.getDevice(getProperties().getProperty("appid"), devEui) + .execute(); if (response.isSuccessful()) { Device device = response.body(); result.setResult(new EndDevice(devEui, device.getTitle(), device.getDevclass())); @@ -166,7 +168,8 @@ public LNSResponse sendDownlink(DownlinkData operation) { LNSResponse result = new LNSResponse().withOk(true); String token = null; try { - retrofit2.Response> response = loriotService.getTokens(getProperties().getProperty("appid")).execute(); + retrofit2.Response> response = loriotService.getTokens(getProperties().getProperty("appid")) + .execute(); if (response.isSuccessful()) { token = response.body().iterator().next(); } @@ -192,7 +195,7 @@ public LNSResponse sendDownlink(DownlinkData operation) { if (response != null) { result.setResult(response.getSeqdn()); } - } catch(HttpClientErrorException e) { + } catch (HttpClientErrorException e) { e.printStackTrace(); result.withOk(false).withMessage(e.getResponseBodyAsString()); } @@ -241,7 +244,8 @@ private LNSResponse provisionDeviceOtaa(DeviceProvisioning deviceProvision deviceCreate.setDevclass(Optional.ofNullable(deviceProvisioning.getDeviceClass().name()).orElse("A")); try { - retrofit2.Response response = loriotService.createDeviceOtaa(getProperties().getProperty("appid"), deviceCreate).execute(); + retrofit2.Response response = loriotService + .createDeviceOtaa(getProperties().getProperty("appid"), deviceCreate).execute(); if (!response.isSuccessful()) { result.withOk(false).withMessage(response.errorBody().string()); } @@ -264,7 +268,8 @@ private LNSResponse provisionDeviceAbp(DeviceProvisioning deviceProvisioni deviceCreate.setDevclass(Optional.ofNullable(deviceProvisioning.getDeviceClass().name()).orElse("A")); try { - retrofit2.Response response = loriotService.createDeviceAbp(getProperties().getProperty("appid"), deviceCreate).execute(); + retrofit2.Response response = loriotService + .createDeviceAbp(getProperties().getProperty("appid"), deviceCreate).execute(); if (!response.isSuccessful()) { result.withOk(false).withMessage(response.errorBody().string()); } @@ -276,8 +281,8 @@ private LNSResponse provisionDeviceAbp(DeviceProvisioning deviceProvisioni } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { - LNSResponse> result = new LNSResponse>().withOk(true); + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { + LNSResponse result = new LNSResponse().withOk(true); logger.info("Configuring routings to: {} with credentials: {}:{}", url, login, password); try { retrofit2.Response> outputs = loriotService.getOutputs(getProperties().getProperty("appid")) @@ -295,7 +300,8 @@ public LNSResponse> configureRoutings(String url, String tenant, St } HttpPush httpPush = new HttpPush(url + "/uplink", "Basic " + Base64.getEncoder().encodeToString((tenant + "/" + login + ":" + password).getBytes())); - retrofit2.Response response = loriotService.createHttpPush(getProperties().getProperty("appid"), httpPush).execute(); + retrofit2.Response response = loriotService + .createHttpPush(getProperties().getProperty("appid"), httpPush).execute(); if (!response.isSuccessful()) { result.withOk(false).withMessage(response.errorBody().string()); } @@ -307,7 +313,7 @@ public LNSResponse> configureRoutings(String url, String tenant, St } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { + public LNSResponse removeRoutings() { // TODO return new LNSResponse().withOk(false).withMessage("Not implemented"); } @@ -328,7 +334,8 @@ public List getApps() { public LNSResponse deprovisionDevice(String deveui) { LNSResponse result = new LNSResponse().withOk(true); try { - retrofit2.Response response = loriotService.removeDevice(getProperties().getProperty("appid"), deveui).execute(); + retrofit2.Response response = loriotService + .removeDevice(getProperties().getProperty("appid"), deveui).execute(); if (!response.isSuccessful()) { result.withOk(false).withMessage(response.errorBody().string()); } @@ -351,4 +358,8 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga public LNSResponse deprovisionGateway(String id) { return new LNSResponse().withOk(false).withMessage("Not implemented"); } + + public boolean hasGatewayManagementCapability() { + return false; + } } diff --git a/lora-ns-ms/pom.xml b/lora-ns-ms/pom.xml index f158acbcc..818cd028a 100644 --- a/lora-ns-ms/pom.xml +++ b/lora-ns-ms/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - + com.softwareag lora @@ -24,7 +24,7 @@ 1.18.22 provided - + org.springframework.boot spring-boot-starter-thymeleaf 2.6.4 diff --git a/lora-ns-ms/src/main/java/lora/ns/connector/LNSAbstractConnector.java b/lora-ns-ms/src/main/java/lora/ns/connector/LNSAbstractConnector.java index e56dbf2b5..6e7c64e11 100644 --- a/lora-ns-ms/src/main/java/lora/ns/connector/LNSAbstractConnector.java +++ b/lora-ns-ms/src/main/java/lora/ns/connector/LNSAbstractConnector.java @@ -1,30 +1,73 @@ package lora.ns.connector; +import java.util.Optional; import java.util.Properties; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +import com.cumulocity.model.idtype.GId; import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; +import com.cumulocity.rest.representation.tenant.OptionRepresentation; +import com.cumulocity.sdk.client.inventory.InventoryApi; +import com.cumulocity.sdk.client.option.TenantOptionApi; import lombok.extern.slf4j.Slf4j; import lora.ns.integration.LNSIntegrationService; @Slf4j -public abstract class LNSAbstractConnector implements LNSConnector { +public abstract class LNSAbstractConnector implements LNSConnector, InitializingBean { protected Properties properties = new Properties(); protected String id; protected String name; protected String type; + @Autowired + protected InventoryApi inventoryApi; + + @Autowired + private TenantOptionApi tenantOptionApi; + protected LNSAbstractConnector(Properties properties) { this.setProperties(properties); } - + protected LNSAbstractConnector(ManagedObjectRepresentation instance) { this.id = instance.getId().getValue(); this.name = instance.getName(); this.type = instance.getProperty(LNSIntegrationService.LNS_TYPE).toString(); } + protected Optional getProperty(String key) { + Object result = null; + if (properties.containsKey(key)) { + result = properties.get(key); + } else { + ManagedObjectRepresentation mor = inventoryApi.get(GId.asGId(this.getId())); + if (mor.hasProperty(key)) { + result = mor.getProperty(key); + } + } + return Optional.ofNullable(result); + } + + protected void setProperty(String key, Object value) { + ManagedObjectRepresentation mor = new ManagedObjectRepresentation(); + mor.setId(GId.asGId(this.getId())); + mor.setProperty(key, value); + this.inventoryApi.update(mor); + } + + @Override + public void afterPropertiesSet() { + log.info("Connector initialized, loading properties from tenant..."); + for (OptionRepresentation option : tenantOptionApi.getAllOptionsForCategory(this.getId())) { + this.properties.setProperty(option.getKey(), option.getValue()); + } + init(); + } + @Override public String getId() { return this.id; diff --git a/lora-ns-ms/src/main/java/lora/ns/connector/LNSConnector.java b/lora-ns-ms/src/main/java/lora/ns/connector/LNSConnector.java index 89631be4d..10a85c8ad 100644 --- a/lora-ns-ms/src/main/java/lora/ns/connector/LNSConnector.java +++ b/lora-ns-ms/src/main/java/lora/ns/connector/LNSConnector.java @@ -11,19 +11,36 @@ public interface LNSConnector { String getId(); + String getType(); + String getName(); + + boolean hasGatewayManagementCapability(); + LNSResponse> getDevices(); + void setProperties(Properties properties); + Properties getProperties(); + LNSResponse getDevice(String devEui); + LNSResponse sendDownlink(DownlinkData downlinkData); + LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning); - LNSResponse> configureRoutings(String url, String tenant, String login, String password); - LNSResponse removeRoutings(String tenant, List routeIds); + + LNSResponse configureRoutings(String url, String tenant, String login, String password); + + LNSResponse removeRoutings(); + LNSResponse deprovisionDevice(String deveui); + LNSResponse> getGateways(); - Properties mergeProperties(Properties properties); + + Properties mergeProperties(Properties properties); + LNSResponse provisionGateway(GatewayProvisioning gatewayProvisioning); + LNSResponse deprovisionGateway(String id); } diff --git a/lora-ns-ms/src/main/java/lora/ns/integration/LNSIntegrationService.java b/lora-ns-ms/src/main/java/lora/ns/integration/LNSIntegrationService.java index d758ec5f0..082e21405 100644 --- a/lora-ns-ms/src/main/java/lora/ns/integration/LNSIntegrationService.java +++ b/lora-ns-ms/src/main/java/lora/ns/integration/LNSIntegrationService.java @@ -19,6 +19,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; import org.springframework.scheduling.Trigger; @@ -115,8 +117,8 @@ public abstract class LNSIntegrationService { @Autowired private LNSGatewayManager lnsGatewayManager; - @Autowired - protected SpringTemplateEngine mMessageTemplateEngine; + @Autowired + protected SpringTemplateEngine mMessageTemplateEngine; protected LinkedList wizard = new LinkedList<>(); @@ -147,17 +149,24 @@ public String getSimulatedPayload(Map fields) { return mMessageTemplateEngine.process("payload.json", context); } + @Autowired + private ApplicationContext applicationContext; + protected I getInstance(ManagedObjectRepresentation instance) { @SuppressWarnings("unchecked") Class instanceType = (Class) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; I result = null; + AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); try { result = instanceType.getConstructor(instance.getClass()).newInstance(instance); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { e.printStackTrace(); } + beanFactory.autowireBean(result); + beanFactory.initializeBean(result, "connector-" + result.getId()); + return result; } @@ -180,14 +189,6 @@ private void init(MicroserviceSubscriptionAddedEvent event) { for (ManagedObjectRepresentation mor : col.get(queryParam).allPages()) { logger.info("Retrieved connector: {} of type {}", mor.getName(), mor.getProperty(LNS_TYPE)); LNSConnector instance = getInstance(mor); - Properties properties = new Properties(); - for (OptionRepresentation option : tenantOptionApi.getAllOptionsForCategory(instance.getId())) { - properties.setProperty(option.getKey(), option.getValue()); - } - instance.setProperties(properties); - if (mor.hasProperty("routeIds")) { - instance.getProperties().put("routeIds", mor.getProperty("routeIds")); - } lnsConnectorManager.addConnector(instance); lnsGatewayManager.upsertGateways(instance); configureRoutings(instance.getId(), event.getCredentials()); @@ -242,7 +243,8 @@ public void updateOperation(String event, String lnsInstanceId) { } public LNSResponse> getDevices(String lnsInstanceId) { - LNSResponse> result = new LNSResponse<>("No connector found with id " + lnsInstanceId, false, null); + LNSResponse> result = new LNSResponse<>("No connector found with id " + lnsInstanceId, false, + null); Optional connector = lnsConnectorManager.getConnector(lnsInstanceId); if (connector.isPresent()) { result = connector.get().getDevices(); @@ -256,12 +258,9 @@ private void configureRoutings(String lnsInstanceId, MicroserviceCredentials cre logger.info("Connector URL is {}", url); Optional connector = lnsConnectorManager.getConnector(lnsInstanceId); if (connector.isPresent()) { - var response = connector.get().configureRoutings(url, subscriptionsService.getTenant(), credentials.getUsername(), + connector.get().configureRoutings(url, subscriptionsService.getTenant(), + credentials.getUsername(), credentials.getPassword()); - ManagedObjectRepresentation mor = new ManagedObjectRepresentation(); - mor.setId(GId.asGId(connector.get().getId())); - mor.setProperty("routeIds", response.getResult()); - inventoryApi.update(mor); } } @@ -305,29 +304,18 @@ public ManagedObjectRepresentation addLnsConnector(LNSConnectorRepresentation co return mor; } - private List getRouteIds(LNSConnector connector) { - List result = new ArrayList<>(); - ManagedObjectRepresentation mor = inventoryApi.get(GId.asGId(connector.getId())); - if (mor.hasProperty("routeIds")) { - Object p = mor.getProperty("routeIds"); - if (p instanceof List) { - result = (List)p; - } - } - - return result; - } - public void removeLnsConnector(String lnsConnectorId) { Optional connector = lnsConnectorManager.getConnector(lnsConnectorId); if (connector.isPresent()) { - connector.get().removeRoutings(subscriptionsService.getTenant(), getRouteIds(connector.get())); + connector.get().removeRoutings(); inventoryApi.delete(new GId(lnsConnectorId)); lnsConnectorManager.removeConnector(lnsConnectorId); + AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); + beanFactory.destroyBean(beanFactory.getBean("connector-" + lnsConnectorId)); } } - public void updateLnsConnector(String lnsConnectorId, Properties properties) { + public void updateLnsConnector(String lnsConnectorId, Properties properties) { Optional connector = lnsConnectorManager.getConnector(lnsConnectorId); if (connector.isPresent()) { LNSConnector c = connector.get(); @@ -344,28 +332,33 @@ public void updateLnsConnector(String lnsConnectorId, Properties properties) { tenantOptionApi.save(option); }); } - } + } private boolean isPropertyEncrypted(String key) { boolean[] result = { false }; wizard.forEach(step -> step.getPropertyDescriptions().forEach(p -> { - if (p.getName().equals(key)) { - result[0] = p.isEncrypted(); - } - }) - ); + if (p.getName().equals(key)) { + result[0] = p.isEncrypted(); + } + })); return result[0]; } - //@Scheduled(initialDelay = 10000, fixedDelay = 300000) + @Scheduled(initialDelay = 10000, fixedDelay = 300000) private void scanGateways() { subscriptionsService.runForEachTenant(() -> { - logger.info("Scanning gateways in tenant {}", subscriptionsService.getTenant()); Map connectors = lnsConnectorManager.getConnectors(); if (connectors != null) { - connectors.values().forEach(c -> lnsGatewayManager.upsertGateways(c)); + connectors.values().forEach(c -> { + if (c.hasGatewayManagementCapability()) { + logger.info("Scanning gateways in tenant {} with connector {}", + subscriptionsService.getTenant(), + c.getName()); + lnsGatewayManager.upsertGateways(c); + } + }); } }); } @@ -382,7 +375,8 @@ private void processPendingOperations() { && !opCollectionRepresentation.getOperations() .isEmpty(); opCollectionRepresentation = oc .getNextPage(opCollectionRepresentation)) { - logger.info("Processing pending operations on tenant {} - page {}", currentTenant, oc.get().getPageStatistics().getCurrentPage()); + logger.info("Processing pending operations on tenant {} - page {}", currentTenant, + oc.get().getPageStatistics().getCurrentPage()); for (OperationRepresentation op : opCollectionRepresentation.getOperations()) { lnsOperationManager.executePending(op); } @@ -437,7 +431,7 @@ public ThreadPoolTaskScheduler taskScheduler() { taskScheduler.schedule(this::scanGateways, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { - Calendar nextExecutionTime = new GregorianCalendar(); + Calendar nextExecutionTime = new GregorianCalendar(); Date lastActualExecutionTime = triggerContext.lastActualExecutionTime(); if (lastActualExecutionTime != null) { nextExecutionTime.setTime(lastActualExecutionTime); diff --git a/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java b/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java index 99dbdfa5a..a66d079bf 100644 --- a/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java +++ b/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java @@ -5,15 +5,16 @@ import java.util.ArrayList; import java.util.Base64; import java.util.List; +import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; -import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; +import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; + import c8y.ConnectionState; import lora.codec.downlink.DownlinkData; import lora.codec.uplink.C8YData; @@ -38,11 +39,11 @@ import lora.ns.objenious.rest.ScenarioRoutingCreateUpdate; import lora.ns.objenious.rest.ScenarioRoutingCreateUpdate.FormatTypeEnum; import lora.ns.objenious.rest.ScenarioRoutingCreateUpdate.MessageTypeEnum; -import lora.ns.objenious.rest.ScenarioRoutingReader; import okhttp3.Interceptor; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import okhttp3.ResponseBody; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; @@ -107,7 +108,8 @@ protected void init() { @Override public LNSResponse> getDevices() { - LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList<>()); + LNSResponse> result = new LNSResponse>().withOk(true) + .withResult(new ArrayList<>()); try { retrofit2.Response> response = objeniousService.getDevices().execute(); if (response.isSuccessful()) { @@ -207,7 +209,8 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) deviceCreate.setLat(deviceProvisioning.getLat()); deviceCreate.setLng(deviceProvisioning.getLng()); deviceCreate.setGroupId(Integer.parseInt(properties.getProperty("groupId"))); - deviceCreate.setProfileId(Integer.valueOf(deviceProvisioning.getAdditionalProperties().getProperty("deviceProfile"))); + deviceCreate.setProfileId( + Integer.valueOf(deviceProvisioning.getAdditionalProperties().getProperty("deviceProfile"))); retrofit2.Response response = objeniousService.createDevice(deviceCreate).execute(); if (!response.isSuccessful()) { logger.error(response.errorBody().string()); @@ -241,7 +244,12 @@ public LNSResponse configureRouting(String url, String tenant, String logi LNSResponse result = new LNSResponse().withOk(false); - removeRouting(name); + if (name.endsWith("downlink")) { + this.getProperty("downlinkRouteId") + .ifPresent(downlinkRoutingId -> removeRouting(downlinkRoutingId.toString())); + } else { + this.getProperty("uplinkRouteId").ifPresent(uplinkRoutingId -> removeRouting(uplinkRoutingId.toString())); + } RoutingHttp routingHttp = new RoutingHttp(); routingHttp.setUrl(url); @@ -264,6 +272,12 @@ public LNSResponse configureRouting(String url, String tenant, String logi if (!response.isSuccessful()) { logger.error("Error from Objenious: {}", response.errorBody().string()); result.withOk(false).withMessage(response.errorBody().string()); + } else { + if (name.endsWith("downlink")) { + this.setProperty("downlinkRouteId", response.body().getId()); + } else { + this.setProperty("uplinkRouteId", response.body().getId()); + } } } catch (Exception e) { e.printStackTrace(); @@ -274,12 +288,14 @@ public LNSResponse configureRouting(String url, String tenant, String logi } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { logger.info("Configuring routings to: {} with credentials: {}:{}", url, login, password); - LNSResponse> result = new LNSResponse<>(); - LNSResponse resultDownlink = configureRouting(url + "/downlink", tenant, login, password, tenant + "-" + this.getId() + "-downlink", + LNSResponse result = new LNSResponse<>(); + LNSResponse resultDownlink = configureRouting(url + "/downlink", tenant, login, password, + tenant + "-" + this.getId() + "-downlink", MessageTypeEnum.DOWNLINK); - LNSResponse resultUplink = configureRouting(url + "/uplink", tenant, login, password, tenant + "-" + this.getId() + "-uplink", + LNSResponse resultUplink = configureRouting(url + "/uplink", tenant, login, password, + tenant + "-" + this.getId() + "-uplink", MessageTypeEnum.UPLINK); result.setOk(resultDownlink.isOk() && resultUplink.isOk()); @@ -296,46 +312,42 @@ public LNSResponse> configureRoutings(String url, String tenant, St return result; } - private LNSResponse removeRouting(String name) { + private LNSResponse removeRouting(String id) { LNSResponse result = new LNSResponse().withOk(true); try { - retrofit2.Response> response = objeniousService.getRouting().execute(); - if (response.isSuccessful()) { - List routings = response.body(); - if (routings != null) { - routings.stream().filter(routing -> routing.getName().equals(name)).forEach(routing -> { - try { - objeniousService.deleteRouting(routing.getId()).execute(); - } catch (Exception e) { - e.printStackTrace(); - } - }); - } - } else { + retrofit2.Response response = objeniousService.deleteRouting(Integer.parseInt(id)).execute(); + if (!response.isSuccessful()) { + logger.error("Error from Objenious: {}", response.errorBody().string()); result.withOk(false).withMessage(response.errorBody().string()); } - } catch (Exception e1) { - e1.printStackTrace(); - result.withOk(false).withMessage(e1.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + result.withOk(false).withMessage(e.getMessage()); } return result; } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { - LNSResponse result = new LNSResponse().withOk(true); - var resultUplink = removeRouting(tenant + "-" + this.getId() + "-uplink"); - var resultDownlink = removeRouting(tenant + "-" + this.getId() + "-downlink"); - result.setOk(resultUplink.isOk() && resultDownlink.isOk()); - if (!result.isOk()) { - result.setMessage(""); + public LNSResponse removeRoutings() { + final LNSResponse result = new LNSResponse().withOk(true); + Optional uplinkRouteId = getProperty("uplinkRouteId"); + Optional downlinkRouteId = getProperty("downlinkRouteId"); + uplinkRouteId.ifPresent(id -> { + var resultUplink = removeRouting(id.toString()); + result.setOk(resultUplink.isOk()); + result.setMessage(resultUplink.getMessage()); + }); + downlinkRouteId.ifPresent(id -> { + var resultDownlink = removeRouting(id.toString()); + result.setOk(result.isOk() && resultDownlink.isOk()); if (!resultDownlink.isOk()) { - result.setMessage(resultDownlink.getMessage()); - } - if (!resultUplink.isOk()) { - result.setMessage(result.getMessage() + "\n" + resultUplink.getMessage()); + if (!result.isOk()) { + result.setMessage(result.getMessage() + "\n" + resultDownlink.getMessage()); + } else { + result.setMessage(resultDownlink.getMessage()); + } } - } + }); return result; } @@ -371,28 +383,34 @@ public LNSResponse> getGateways() { logger.info("Getting list of gateways with connector {}...", this.getName()); LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList<>()); try { - retrofit2.Response> response = objeniousService.getGateways().execute(); + retrofit2.Response> response = objeniousService.getGateways() + .execute(); if (response.isSuccessful()) { - response.body().forEach(g -> { - logger.info("Got gateway {}", g.getGatewayName()); - C8YData data = new C8YData(); - ConnectionState state = ConnectionState.AVAILABLE; - switch(g.getStatus()) { - case ACTIVE: - state = ConnectionState.AVAILABLE; - break; - case ALERT: - state = ConnectionState.AVAILABLE; - break; - case INACTIVE: - state = ConnectionState.UNAVAILABLE; - break; - default: - break; - } - Gateway gateway = new Gateway(g.getGatewayId(), g.getSerialNumber(), g.getGatewayName(), BigDecimal.valueOf(g.getLat()), BigDecimal.valueOf(g.getLng()), g.getGatewayType(), state, data); - result.getResult().add(gateway); - }); + if (response.body() != null) { + response.body().forEach(g -> { + logger.info("Got gateway {}", g.getGatewayName()); + C8YData data = new C8YData(); + ConnectionState state = ConnectionState.AVAILABLE; + switch (g.getStatus()) { + case ACTIVE: + state = ConnectionState.AVAILABLE; + break; + case ALERT: + state = ConnectionState.AVAILABLE; + break; + case INACTIVE: + state = ConnectionState.UNAVAILABLE; + break; + default: + break; + } + Gateway gateway = new Gateway(g.getGatewayId(), g.getSerialNumber(), g.getGatewayName(), + BigDecimal.valueOf(g.getLat()), BigDecimal.valueOf(g.getLng()), g.getGatewayType(), + state, + data); + result.getResult().add(gateway); + }); + } } else { logger.error("Couldn't retrieve gateways with connector {}", this.getName()); result.withOk(false).withMessage(response.errorBody().string()); @@ -424,4 +442,8 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga public LNSResponse deprovisionGateway(String id) { return new LNSResponse().withOk(false).withMessage("Not supported"); } + + public boolean hasGatewayManagementCapability() { + return false; + } } diff --git a/lora-ns-orbiwise/src/main/java/lora/ns/orbiwise/OrbiwiseConnector.java b/lora-ns-orbiwise/src/main/java/lora/ns/orbiwise/OrbiwiseConnector.java index 6be140d1d..0abf8bd66 100644 --- a/lora-ns-orbiwise/src/main/java/lora/ns/orbiwise/OrbiwiseConnector.java +++ b/lora-ns-orbiwise/src/main/java/lora/ns/orbiwise/OrbiwiseConnector.java @@ -103,7 +103,8 @@ protected void init() { @Override public LNSResponse> getDevices() { - LNSResponse> result = new LNSResponse>().withOk(true).withResult(new ArrayList<>()); + LNSResponse> result = new LNSResponse>().withOk(true) + .withResult(new ArrayList<>()); try { var response = orbiwiseService.getDevices().execute(); if (response.isSuccessful()) { @@ -132,7 +133,8 @@ public LNSResponse getDevice(String devEui) { var response = orbiwiseService.getDevice(devEui).execute(); if (response.isSuccessful()) { Device device = response.body(); - result.setResult(new EndDevice(devEui, devEui, DeviceClass.BY_VALUE.get(device.getLora_device_class()).name())); + result.setResult( + new EndDevice(devEui, devEui, DeviceClass.BY_VALUE.get(device.getLora_device_class()).name())); } else { result.withOk(false).withMessage(response.errorBody().string()); } @@ -191,10 +193,11 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) return result; } - public LNSResponse> configureRouting(String url, String tenant, String login, String password, String subscriptions) { + public LNSResponse configureRouting(String url, String tenant, String login, String password, + String subscriptions) { assert orbiwiseService != null : "orbiwiseService is not initialized"; - LNSResponse> result = new LNSResponse>().withOk(true); + LNSResponse result = new LNSResponse().withOk(true); Pushmode pushmode = new Pushmode(); pushmode.setAuth_string( @@ -228,13 +231,13 @@ public LNSResponse> configureRouting(String url, String tenant, Str } @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { logger.info("Configuring routings to: {} with credentials: {}:{}", url, login, password); return configureRouting(url, tenant, login, password, "payloads_dl,payloads_ul"); } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { + public LNSResponse removeRoutings() { LNSResponse result = new LNSResponse().withOk(true); try { var response = orbiwiseService.stopRouting().execute(); @@ -275,4 +278,9 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga public LNSResponse deprovisionGateway(String id) { return new LNSResponse().withOk(false).withMessage("Not implemented."); } + + @Override + public boolean hasGatewayManagementCapability() { + return false; + } } diff --git a/lora-ns-ttn/pom.xml b/lora-ns-ttn/pom.xml index eac2733b7..fb85009a4 100644 --- a/lora-ns-ttn/pom.xml +++ b/lora-ns-ttn/pom.xml @@ -12,10 +12,9 @@ lora-ns-ttn - 1.37.0 + 1.51.0 1.6.1 0.6.1 - 0.3.0 @@ -45,16 +44,6 @@ grpc-stub ${io.grpc.version} - - com.github.saikocat - gogoproto-java - 1.0.1 - - - io.envoyproxy.protoc-gen-validate - pgv-java-stub - ${pgv.version} - com.fasterxml.jackson.datatype jackson-datatype-joda @@ -85,17 +74,12 @@ ${protobuf-maven-plugin.version} - com.google.protobuf:protoc:3.12.4:exe:${os.detected.classifier} + com.google.protobuf:protoc:3.21.11:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier} - - - ${user.home}/go/src/ - - diff --git a/lora-ns-ttn/readme.md b/lora-ns-ttn/readme.md index 5d0099b85..e6fe9c4cf 100644 --- a/lora-ns-ttn/readme.md +++ b/lora-ns-ttn/readme.md @@ -1,17 +1,13 @@ # The Things Network (TTN) LNS Integration + This is a LNS intgration for the Cumulocity LoRa framework (https://github.com/SoftwareAG/cumulocity-lora) to support the The Things Network LNS (https://www.thethingsnetwork.org/). It is implemented as a Spring Boot application in Java. ## Build & Deploy -The TTN Lora LNS implementation requires an installation of Go to compile the Google Protocol Buffer schemas. The "setenv.sh" script configures the Go environment and needs to be run before building the microservice: - -``` -. ./setenv.sh -mvn package -``` -The resulting microservice can be deployed either -* manually via the Cumulocity Administration application or -* automated using the Cumulocity Microservice Maven Plugin's upload task (https://cumulocity.com/guides/microservice-sdk/java/#maven-plugin) or -* automated using the Cumulocity Microservice Utility tool (https://cumulocity.com/guides/microservice-sdk/concept/#ms-utility-tool). +Go environment is not required any more. +The resulting microservice can be deployed either +- manually via the Cumulocity Administration application or +- automated using the Cumulocity Microservice Maven Plugin's upload task (https://cumulocity.com/guides/microservice-sdk/java/#maven-plugin) or +- automated using the Cumulocity Microservice Utility tool (https://cumulocity.com/guides/microservice-sdk/concept/#ms-utility-tool). diff --git a/lora-ns-ttn/setenv.sh b/lora-ns-ttn/setenv.sh deleted file mode 100644 index e1a8f7300..000000000 --- a/lora-ns-ttn/setenv.sh +++ /dev/null @@ -1,4 +0,0 @@ -export GOPATH=$HOME/go -export GO111MODULE=off -go get -d github.com/envoyproxy/protoc-gen-validate/validate -go get -d github.com/gogo/protobuf/gogoproto \ No newline at end of file diff --git a/lora-ns-ttn/src/main/java/lora/App.java b/lora-ns-ttn/src/main/java/lora/App.java index a846b0971..2ec76025a 100644 --- a/lora-ns-ttn/src/main/java/lora/App.java +++ b/lora-ns-ttn/src/main/java/lora/App.java @@ -12,10 +12,10 @@ public class App { @Autowired LNSRestController restController; - + @Autowired - TTNRestController orbiwanRestController; - + TTNRestController ttnRestController; + public static void main(String[] args) { SpringApplication.run(App.class, args); } diff --git a/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java b/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java index bac0a03a2..0c36e68cf 100644 --- a/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java +++ b/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java @@ -3,24 +3,25 @@ import java.util.ArrayList; import java.util.Base64; import java.util.List; -import java.util.Optional; import java.util.Properties; import java.util.UUID; import java.util.stream.Collectors; import javax.net.ssl.SSLException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import com.cumulocity.microservice.subscription.service.MicroserviceSubscriptionsService; import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; import com.google.common.io.BaseEncoding; import com.google.protobuf.ByteString; -import com.google.protobuf.Empty; import com.google.protobuf.FieldMask; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import c8y.ConnectionState; import io.grpc.ManagedChannel; +import io.grpc.StatusRuntimeException; import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; import lora.codec.downlink.DownlinkData; @@ -32,11 +33,9 @@ import lora.ns.gateway.Gateway; import ttn.lorawan.v3.AppAsGrpc; import ttn.lorawan.v3.AppAsGrpc.AppAsBlockingStub; -import ttn.lorawan.v3.ApplicationAccessGrpc; -import ttn.lorawan.v3.ApplicationAccessGrpc.ApplicationAccessBlockingStub; import ttn.lorawan.v3.ApplicationOuterClass.Application; import ttn.lorawan.v3.ApplicationOuterClass.Applications; -import ttn.lorawan.v3.ApplicationOuterClass.ListApplicationCollaboratorsRequest; +import ttn.lorawan.v3.ApplicationOuterClass.GetApplicationRequest; import ttn.lorawan.v3.ApplicationOuterClass.ListApplicationsRequest; import ttn.lorawan.v3.ApplicationRegistryGrpc; import ttn.lorawan.v3.ApplicationRegistryGrpc.ApplicationRegistryBlockingStub; @@ -61,10 +60,13 @@ import ttn.lorawan.v3.EndDeviceRegistryGrpc.EndDeviceRegistryBlockingStub; import ttn.lorawan.v3.GatewayOuterClass; import ttn.lorawan.v3.GatewayOuterClass.CreateGatewayRequest; +import ttn.lorawan.v3.GatewayOuterClass.GatewayConnectionStats; import ttn.lorawan.v3.GatewayOuterClass.Gateways; import ttn.lorawan.v3.GatewayOuterClass.ListGatewaysRequest; import ttn.lorawan.v3.GatewayRegistryGrpc; import ttn.lorawan.v3.GatewayRegistryGrpc.GatewayRegistryBlockingStub; +import ttn.lorawan.v3.GsGrpc; +import ttn.lorawan.v3.GsGrpc.GsBlockingStub; import ttn.lorawan.v3.Identifiers.ApplicationIdentifiers; import ttn.lorawan.v3.Identifiers.EndDeviceIdentifiers; import ttn.lorawan.v3.Identifiers.GatewayIdentifiers; @@ -79,9 +81,6 @@ import ttn.lorawan.v3.Messages.DownlinkQueueRequest; import ttn.lorawan.v3.NsEndDeviceRegistryGrpc; import ttn.lorawan.v3.NsEndDeviceRegistryGrpc.NsEndDeviceRegistryBlockingStub; -import ttn.lorawan.v3.RightsOuterClass.Collaborator; -import ttn.lorawan.v3.RightsOuterClass.Collaborators; -import ttn.lorawan.v3.RightsOuterClass.Right; public class TTNConnector extends LNSAbstractConnector { @@ -141,18 +140,19 @@ public LNSResponse getDevice(String devEui) { try { EndDeviceRegistryBlockingStub service = EndDeviceRegistryGrpc.newBlockingStub(managedChannel) .withCallCredentials(token); - + ttn.lorawan.v3.EndDeviceOuterClass.EndDevice device = service .get(GetEndDeviceRequest.newBuilder().setEndDeviceIds(getDeviceIds(devEui)) - .setFieldMask(FieldMask.newBuilder().addPaths("name").build()).build()); - + .setFieldMask(FieldMask.newBuilder().addPaths("name").build()) + .build()); + EndDevice endDevice = new EndDevice(devEui, device.getName(), device.getSupportsClassC() ? "C" : device.getSupportsClassB() ? "B" : "A"); result.setOk(true); result.setResult(endDevice); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.setOk(false); result.setMessage(e.getMessage()); @@ -172,16 +172,18 @@ public LNSResponse sendDownlink(DownlinkData operation) { ApplicationDownlink downlink = ApplicationDownlink.newBuilder().setFPort(operation.getFport()) .setConfirmed(true) .setFrmPayload(ByteString.copyFrom( - BaseEncoding.base16().decode(operation.getPayload().toUpperCase()))) + BaseEncoding.base16() + .decode(operation.getPayload().toUpperCase()))) .addCorrelationIds("c8y:" + downlinkCorrelationId).build(); - - AppAsBlockingStub asService = AppAsGrpc.newBlockingStub(managedChannel).withCallCredentials(token); - + + AppAsBlockingStub asService = AppAsGrpc.newBlockingStub(managedChannel) + .withCallCredentials(token); + asService.downlinkQueuePush(DownlinkQueueRequest.newBuilder().addDownlinks(downlink) .setEndDeviceIds(getDeviceIds(operation.getDevEui())).build()); result.setOk(true); result.setResult(downlinkCorrelationId); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.setOk(false); result.setMessage(e.getMessage()); @@ -224,9 +226,11 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) .setNetworkServerAddress(properties.getProperty("address")) .setSupportsJoin(true) .setLorawanVersion(MACVersion.valueOf( - deviceProvisioning.getAdditionalProperties().getProperty("MACVersion"))) + deviceProvisioning.getAdditionalProperties() + .getProperty("MACVersion"))) .setLorawanPhyVersion(PHYVersion.valueOf( - deviceProvisioning.getAdditionalProperties().getProperty("PHYVersion"))) + deviceProvisioning.getAdditionalProperties() + .getProperty("PHYVersion"))) .setFrequencyPlanId(deviceProvisioning.getAdditionalProperties() .getProperty("frequencyPlan")) .setIds(EndDeviceIdentifiers.newBuilder() @@ -236,11 +240,13 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) .build()) .setDevEui(ByteString .copyFrom(BaseEncoding.base16() - .decode(deviceProvisioning.getDevEUI() + .decode(deviceProvisioning + .getDevEUI() .toUpperCase()))) .setJoinEui(ByteString .copyFrom(BaseEncoding.base16() - .decode(deviceProvisioning.getAppEUI() + .decode(deviceProvisioning + .getAppEUI() .toUpperCase()))) .build()) .setRootKeys(RootKeys.newBuilder() @@ -282,11 +288,14 @@ public LNSResponse provisionDevice(DeviceProvisioning deviceProvisioning) return result; } + @Autowired + private MicroserviceSubscriptionsService subscriptionsService; + @Override - public LNSResponse> configureRoutings(String url, String tenant, String login, String password) { + public LNSResponse configureRoutings(String url, String tenant, String login, String password) { logger.info("Configuring routings to: {} with credentials: {}:{} on TTN app {}", url, login, password, properties.getProperty(APPID)); - LNSResponse> result = new LNSResponse<>(); + LNSResponse result = new LNSResponse<>(); try { ApplicationWebhookRegistryBlockingStub app = ApplicationWebhookRegistryGrpc @@ -299,18 +308,22 @@ public LNSResponse> configureRoutings(String url, String tenant, St .setApplicationId(properties.getProperty(APPID)) .build()) .build()) - .setBaseUrl(url).setUplinkMessage(Message.newBuilder().setPath("/uplink").build()) + .setBaseUrl(url) + .setUplinkMessage(Message.newBuilder().setPath("/uplink").build()) .setDownlinkAck(Message.newBuilder().setPath("/downlink").build()) .setDownlinkNack(Message.newBuilder().setPath("/downlink").build()) .setDownlinkSent(Message.newBuilder().setPath("/downlink").build()) .setDownlinkFailed(Message.newBuilder().setPath("/downlink").build()) - .setDownlinkQueued(Message.newBuilder().setPath("/downlink").build()).setFormat("json") + .setDownlinkQueued(Message.newBuilder().setPath("/downlink").build()) + .setFormat("json") .putHeaders("Authorization", "Basic " + Base64.getEncoder().encodeToString( - (tenant + "/" + login + ":" + password).getBytes())) + (tenant + "/" + login + ":" + password) + .getBytes())) .build(); app.set(SetApplicationWebhookRequest.newBuilder().setWebhook(webhook) - .setFieldMask(FieldMask.newBuilder().addPaths("base_url").addPaths("downlink_ack") + .setFieldMask(FieldMask.newBuilder().addPaths("base_url") + .addPaths("downlink_ack") .addPaths("downlink_api_key").addPaths("downlink_failed") .addPaths("downlink_nack") .addPaths("downlink_queued").addPaths("downlink_sent") @@ -322,7 +335,7 @@ public LNSResponse> configureRoutings(String url, String tenant, St .addPaths("service_data").addPaths("uplink_message").build()) .build()); result.setOk(true); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.setOk(false); result.setMessage(e.getMessage()); @@ -332,14 +345,15 @@ public LNSResponse> configureRoutings(String url, String tenant, St } @Override - public LNSResponse removeRoutings(String tenant, List routeIds) { + public LNSResponse removeRoutings() { LNSResponse result = new LNSResponse<>(); try { ApplicationWebhookRegistryBlockingStub app = ApplicationWebhookRegistryGrpc .newBlockingStub(managedChannel) .withCallCredentials(token); - - app.delete(ApplicationWebhookIdentifiers.newBuilder().setWebhookId(tenant + "-" + this.getId()) + + app.delete(ApplicationWebhookIdentifiers.newBuilder() + .setWebhookId(subscriptionsService.getTenant() + "-" + this.getId()) .build()); result.setOk(true); } catch (Exception e) { @@ -355,23 +369,26 @@ public LNSResponse removeRoutings(String tenant, List routeIds) { public LNSResponse deprovisionDevice(String deveui) { LNSResponse result = new LNSResponse<>(); try { - JsEndDeviceRegistryBlockingStub service1 = JsEndDeviceRegistryGrpc.newBlockingStub(managedChannel) + JsEndDeviceRegistryBlockingStub service1 = JsEndDeviceRegistryGrpc + .newBlockingStub(managedChannel) .withCallCredentials(token); - AsEndDeviceRegistryBlockingStub service2 = AsEndDeviceRegistryGrpc.newBlockingStub(managedChannel) + AsEndDeviceRegistryBlockingStub service2 = AsEndDeviceRegistryGrpc + .newBlockingStub(managedChannel) .withCallCredentials(token); - NsEndDeviceRegistryBlockingStub service3 = NsEndDeviceRegistryGrpc.newBlockingStub(managedChannel) + NsEndDeviceRegistryBlockingStub service3 = NsEndDeviceRegistryGrpc + .newBlockingStub(managedChannel) .withCallCredentials(token); EndDeviceRegistryBlockingStub service4 = EndDeviceRegistryGrpc.newBlockingStub(managedChannel) .withCallCredentials(token); - + EndDeviceIdentifiers ids = getDeviceIds(deveui); - + service2.delete(ids); service3.delete(ids); service1.delete(ids); service4.delete(ids); result.setOk(true); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.setOk(false); result.setMessage(e.getMessage()); @@ -388,11 +405,25 @@ public LNSResponse> getGateways() { .newBlockingStub(managedChannel).withCallCredentials(token); ListGatewaysRequest request = ListGatewaysRequest.newBuilder().build(); Gateways gateways = gatewayRegistry.list(request); + final GsBlockingStub gatewayServer = GsGrpc.newBlockingStub(managedChannel) + .withCallCredentials(token); result.setOk(true); result.setResult(gateways.getGatewaysList().stream().map(g -> { - return new Gateway(g.getIds().getEui().toStringUtf8(), g.getIds().getGatewayId(), g.getName(), + // logger.info("Retrieved gateway: {}", g.toString()); + boolean connected = true; + try { + GatewayConnectionStats stats = gatewayServer + .getGatewayConnectionStats(g.getIds()); + // logger.info("Gateway status: {}", stats.toString()); + } catch (StatusRuntimeException e) { + // e.printStackTrace(); + connected = false; + } + return new Gateway(g.getIds().getGatewayId(), g.getIds().getGatewayId(), + g.getName(), null, null, g.getVersionIds().getBrandId(), - g.isInitialized() ? ConnectionState.AVAILABLE : ConnectionState.UNAVAILABLE, + connected ? ConnectionState.AVAILABLE + : ConnectionState.UNAVAILABLE, new C8YData()); }).collect(Collectors.toList())); } catch (Exception e) { @@ -415,25 +446,23 @@ public List getApplications() { private OrganizationOrUserIdentifiers getCurrentUserOrOrganization() { OrganizationOrUserIdentifiers result = null; - ApplicationAccessBlockingStub applicationAccess = ApplicationAccessGrpc.newBlockingStub(managedChannel) - .withCallCredentials(token); - Collaborators collaborators = applicationAccess.listCollaborators(ListApplicationCollaboratorsRequest - .newBuilder().setApplicationIds(ApplicationIdentifiers.newBuilder() + ApplicationRegistryBlockingStub applicationRegistry = ApplicationRegistryGrpc + .newBlockingStub(managedChannel).withCallCredentials(token); + Application application = applicationRegistry.get(GetApplicationRequest.newBuilder() + .setApplicationIds(ApplicationIdentifiers.newBuilder() .setApplicationId(properties.getProperty(APPID)).build()) + .setFieldMask(FieldMask.newBuilder().addPaths("administrative_contact") + .addPaths("technical_contact").build()) .build()); - for (Collaborator collaborator : collaborators.getCollaboratorsList()) { - for (Right right : collaborator.getRightsList()) { - if (right == Right.RIGHT_GATEWAY_ALL) { - result = collaborator.getIds(); - } - } - } + + result = application.getAdministrativeContact(); return result; } public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning gatewayProvisioning) { LNSResponse result = new LNSResponse<>(); + if (getCurrentUserOrOrganization() == null) { result.setOk(false); result.setMessage("no user with gateway creation rights was found."); @@ -454,6 +483,7 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga .getAdditionalProperties().getProperty("public"))) .setFrequencyPlanId(gatewayProvisioning.getAdditionalProperties() .getProperty("frequencyPlan")) + .setName(gatewayProvisioning.getName()) .build(); CreateGatewayRequest request = CreateGatewayRequest.newBuilder() .setGateway(gateway) @@ -471,19 +501,23 @@ public LNSResponse provisionGateway(lora.ns.gateway.GatewayProvisioning ga public LNSResponse deprovisionGateway(String id) { LNSResponse result = new LNSResponse<>(); try { - GatewayRegistryBlockingStub gatewayRegistry = GatewayRegistryGrpc.newBlockingStub(managedChannel) + GatewayRegistryBlockingStub gatewayRegistry = GatewayRegistryGrpc + .newBlockingStub(managedChannel) .withCallCredentials(token); gatewayRegistry.delete(GatewayIdentifiers.newBuilder() - .setEui(ByteString - .copyFrom(BaseEncoding.base16().decode(id.toUpperCase()))) + .setGatewayId(id) .build()); result.setOk(true); - } catch(Exception e) { + } catch (Exception e) { e.printStackTrace(); result.setOk(false); result.setMessage(e.getMessage()); } return result; } + + public boolean hasGatewayManagementCapability() { + return true; + } } diff --git a/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto b/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto new file mode 100644 index 000000000..bb07e97fb --- /dev/null +++ b/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto @@ -0,0 +1,103 @@ +// Copyright © 2021 The Things Industries B.V. +// SPDX-License-Identifier: Apache-2.0 + +syntax = "proto2"; + +package thethings.flags; + +option go_package = "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations"; + +import "google/protobuf/descriptor.proto"; + +// NOTE: protoc-gen-go-flags is primarily intended for internal use by +// The Things Industries. We have therefore chosen to use option number 51886, +// which is in the 50000-99999 range reserved for internal use within individual +// organizations. For this reason, the option number is not registered on +// https://github.com/protocolbuffers/protobuf/blob/master/docs/options.md. + +message FileOptions { + +} + +extend google.protobuf.FileOptions { + optional FileOptions file = 51886; +} + +message MessageOptions { + // Generate (field mask) selector flags for this message. + optional bool select = 1; + // Generate setter flags for this message. + optional bool set = 2; + // Treat this message as a wrapper for the value field inside it. + optional bool wrapper = 3; + // Generate semantical flags for this message. Only active when set is also set. + optional bool semantical = 4; +} + +extend google.protobuf.MessageOptions { + optional MessageOptions message = 51886; +} + +message FieldOptions { + // Skip generating (field mask) selector flags for this field. + optional bool select = 1; + // Skip generating setter flags for this field. + optional bool set = 2; + // New flag definition for custom type flag. + // Specified as github.com/username/repo/package.New{CustomType}Flag. + optional string set_flag_new_func = 3; + // Custom value getter from the custom flag type. + // Specified as github.com/username/repo/package.Get{CustomType}FromFlag. + optional string set_flag_getter_func = 4; + // Set flag and all of its subfield flags to hidden. + optional bool hidden = 5; + // Implies field has meaning just by existing. Only active when set is also set. + optional bool semantical = 6; +} + +extend google.protobuf.FieldOptions { + optional FieldOptions field = 51886; +} + +message OneofOptions { + +} + +extend google.protobuf.OneofOptions { + optional OneofOptions oneof = 51886; +} + +message EnumOptions { + // Path where the custom enum values are defined. + // The map must be of type string to int32, where string is an alias name and + // int32 must be a valid enum int32. + optional string alias_map = 1; +} + +extend google.protobuf.EnumOptions { + optional EnumOptions enum = 51886; +} + +message EnumValueOptions { + +} + +extend google.protobuf.EnumValueOptions { + optional EnumValueOptions enum_value = 51886; +} + +message ServiceOptions { + +} + +extend google.protobuf.ServiceOptions { + optional ServiceOptions service = 51886; +} + +message MethodOptions { + +} + +extend google.protobuf.MethodOptions { + optional MethodOptions method = 51886; +} diff --git a/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto b/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto new file mode 100644 index 000000000..0297dd9f7 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto @@ -0,0 +1,105 @@ +// Copyright © 2021 The Things Industries B.V. +// SPDX-License-Identifier: Apache-2.0 + +syntax = "proto2"; + +package thethings.json; + +option go_package = "github.com/TheThingsIndustries/protoc-gen-go-json/annotations"; + +import "google/protobuf/descriptor.proto"; + +// NOTE: protoc-gen-go-json is primarily intended for internal use by +// The Things Industries. We have therefore chosen to use option number 51885, +// which is in the 50000-99999 range reserved for internal use within individual +// organizations. For this reason, the option number is not registered on +// https://github.com/protocolbuffers/protobuf/blob/master/docs/options.md. + +message FileOptions { + // Generate marshalers for all messages and enums in this file. + // Message options can be used to override this. + optional bool marshaler_all = 1; + // Generate unmarshalers for all messages and enums in this file. + // Message options can be used to override this. + optional bool unmarshaler_all = 2; +} + +extend google.protobuf.FileOptions { + optional FileOptions file = 51885; +} + +message MessageOptions { + // Generate a marshaler for this message. + optional bool marshaler = 1; + // Generate an unmarshaler for this message. + optional bool unmarshaler = 2; + // Treat this message as a wrapper for the value field inside it. + optional bool wrapper = 3; +} + +extend google.protobuf.MessageOptions { + optional MessageOptions message = 51885; +} + +message FieldOptions { + // Custom marshaler function. Specified as github.com/username/repo/package.FuncName. + optional string marshaler_func = 1; + // Custom unmarshaler function. Specified as github.com/username/repo/package.FuncName. + optional string unmarshaler_func = 2; +} + +extend google.protobuf.FieldOptions { + optional FieldOptions field = 51885; +} + +message OneofOptions { + +} + +extend google.protobuf.OneofOptions { + optional OneofOptions oneof = 51885; +} + +message EnumOptions { + // Generate a marshaler for this enum. + optional bool marshaler = 1; + // Always marshal as a number. + optional bool marshal_as_number = 2; + // Always marshal as a string. + optional bool marshal_as_string = 3; + // Generate an unmarshaler for this enum. + optional bool unmarshaler = 4; + // Consider string values with this prefix when unmarshaling. + optional string prefix = 5; +} + +extend google.protobuf.EnumOptions { + optional EnumOptions enum = 51885; +} + +message EnumValueOptions { + // The value to emit when marshaling as string. + optional string value = 1; + // Aliases to accept when unmarshaling from string. + repeated string aliases = 2; +} + +extend google.protobuf.EnumValueOptions { + optional EnumValueOptions enum_value = 51885; +} + +message ServiceOptions { + +} + +extend google.protobuf.ServiceOptions { + optional ServiceOptions service = 51885; +} + +message MethodOptions { + +} + +extend google.protobuf.MethodOptions { + optional MethodOptions method = 51885; +} diff --git a/lora-ns-ttn/src/main/proto/github.com/envoyproxy/protoc-gen-validate/validate/validate.proto b/lora-ns-ttn/src/main/proto/github.com/envoyproxy/protoc-gen-validate/validate/validate.proto new file mode 100644 index 000000000..4195ecf9c --- /dev/null +++ b/lora-ns-ttn/src/main/proto/github.com/envoyproxy/protoc-gen-validate/validate/validate.proto @@ -0,0 +1,863 @@ +syntax = "proto2"; +package validate; + +option go_package = "github.com/envoyproxy/protoc-gen-validate/validate"; +option java_package = "io.envoyproxy.pgv.validate"; + +import "google/protobuf/descriptor.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// Validation rules applied at the message level +extend google.protobuf.MessageOptions { + // Disabled nullifies any validation rules for this message, including any + // message fields associated with it that do support validation. + optional bool disabled = 1071; + // Ignore skips generation of validation methods for this message. + optional bool ignored = 1072; +} + +// Validation rules applied at the oneof level +extend google.protobuf.OneofOptions { + // Required ensures that exactly one the field options in a oneof is set; + // validation fails if no fields in the oneof are set. + optional bool required = 1071; +} + +// Validation rules applied at the field level +extend google.protobuf.FieldOptions { + // Rules specify the validations to be performed on this field. By default, + // no validation is performed against a field. + optional FieldRules rules = 1071; +} + +// FieldRules encapsulates the rules for each type of field. Depending on the +// field, the correct set should be used to ensure proper validations. +message FieldRules { + optional MessageRules message = 17; + oneof type { + // Scalar Field Types + FloatRules float = 1; + DoubleRules double = 2; + Int32Rules int32 = 3; + Int64Rules int64 = 4; + UInt32Rules uint32 = 5; + UInt64Rules uint64 = 6; + SInt32Rules sint32 = 7; + SInt64Rules sint64 = 8; + Fixed32Rules fixed32 = 9; + Fixed64Rules fixed64 = 10; + SFixed32Rules sfixed32 = 11; + SFixed64Rules sfixed64 = 12; + BoolRules bool = 13; + StringRules string = 14; + BytesRules bytes = 15; + + // Complex Field Types + EnumRules enum = 16; + RepeatedRules repeated = 18; + MapRules map = 19; + + // Well-Known Field Types + AnyRules any = 20; + DurationRules duration = 21; + TimestampRules timestamp = 22; + } +} + +// FloatRules describes the constraints applied to `float` values +message FloatRules { + // Const specifies that this field must be exactly the specified value + optional float const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional float lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional float lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional float gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional float gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated float in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated float not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// DoubleRules describes the constraints applied to `double` values +message DoubleRules { + // Const specifies that this field must be exactly the specified value + optional double const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional double lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional double lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional double gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional double gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated double in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated double not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int32Rules describes the constraints applied to `int32` values +message Int32Rules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Int64Rules describes the constraints applied to `int64` values +message Int64Rules { + // Const specifies that this field must be exactly the specified value + optional int64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional int64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional int64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional int64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional int64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated int64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt32Rules describes the constraints applied to `uint32` values +message UInt32Rules { + // Const specifies that this field must be exactly the specified value + optional uint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// UInt64Rules describes the constraints applied to `uint64` values +message UInt64Rules { + // Const specifies that this field must be exactly the specified value + optional uint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional uint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional uint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional uint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional uint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated uint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated uint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt32Rules describes the constraints applied to `sint32` values +message SInt32Rules { + // Const specifies that this field must be exactly the specified value + optional sint32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SInt64Rules describes the constraints applied to `sint64` values +message SInt64Rules { + // Const specifies that this field must be exactly the specified value + optional sint64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sint64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sint64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sint64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sint64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sint64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sint64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed32Rules describes the constraints applied to `fixed32` values +message Fixed32Rules { + // Const specifies that this field must be exactly the specified value + optional fixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// Fixed64Rules describes the constraints applied to `fixed64` values +message Fixed64Rules { + // Const specifies that this field must be exactly the specified value + optional fixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional fixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional fixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional fixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional fixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated fixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated fixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed32Rules describes the constraints applied to `sfixed32` values +message SFixed32Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed32 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed32 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed32 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed32 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed32 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed32 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed32 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// SFixed64Rules describes the constraints applied to `sfixed64` values +message SFixed64Rules { + // Const specifies that this field must be exactly the specified value + optional sfixed64 const = 1; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional sfixed64 lt = 2; + + // Lte specifies that this field must be less than or equal to the + // specified value, inclusive + optional sfixed64 lte = 3; + + // Gt specifies that this field must be greater than the specified value, + // exclusive. If the value of Gt is larger than a specified Lt or Lte, the + // range is reversed. + optional sfixed64 gt = 4; + + // Gte specifies that this field must be greater than or equal to the + // specified value, inclusive. If the value of Gte is larger than a + // specified Lt or Lte, the range is reversed. + optional sfixed64 gte = 5; + + // In specifies that this field must be equal to one of the specified + // values + repeated sfixed64 in = 6; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated sfixed64 not_in = 7; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 8; +} + +// BoolRules describes the constraints applied to `bool` values +message BoolRules { + // Const specifies that this field must be exactly the specified value + optional bool const = 1; +} + +// StringRules describe the constraints applied to `string` values +message StringRules { + // Const specifies that this field must be exactly the specified value + optional string const = 1; + + // Len specifies that this field must be the specified number of + // characters (Unicode code points). Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 len = 19; + + // MinLen specifies that this field must be the specified number of + // characters (Unicode code points) at a minimum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of + // characters (Unicode code points) at a maximum. Note that the number of + // characters may differ from the number of bytes in the string. + optional uint64 max_len = 3; + + // LenBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 len_bytes = 20; + + // MinBytes specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_bytes = 4; + + // MaxBytes specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_bytes = 5; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 6; + + // Prefix specifies that this field must have the specified substring at + // the beginning of the string. + optional string prefix = 7; + + // Suffix specifies that this field must have the specified substring at + // the end of the string. + optional string suffix = 8; + + // Contains specifies that this field must have the specified substring + // anywhere in the string. + optional string contains = 9; + + // NotContains specifies that this field cannot have the specified substring + // anywhere in the string. + optional string not_contains = 23; + + // In specifies that this field must be equal to one of the specified + // values + repeated string in = 10; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated string not_in = 11; + + // WellKnown rules provide advanced constraints against common string + // patterns + oneof well_known { + // Email specifies that the field must be a valid email address as + // defined by RFC 5322 + bool email = 12; + + // Hostname specifies that the field must be a valid hostname as + // defined by RFC 1034. This constraint does not support + // internationalized domain names (IDNs). + bool hostname = 13; + + // Ip specifies that the field must be a valid IP (v4 or v6) address. + // Valid IPv6 addresses should not include surrounding square brackets. + bool ip = 14; + + // Ipv4 specifies that the field must be a valid IPv4 address. + bool ipv4 = 15; + + // Ipv6 specifies that the field must be a valid IPv6 address. Valid + // IPv6 addresses should not include surrounding square brackets. + bool ipv6 = 16; + + // Uri specifies that the field must be a valid, absolute URI as defined + // by RFC 3986 + bool uri = 17; + + // UriRef specifies that the field must be a valid URI as defined by RFC + // 3986 and may be relative or absolute. + bool uri_ref = 18; + + // Address specifies that the field must be either a valid hostname as + // defined by RFC 1034 (which does not support internationalized domain + // names or IDNs), or it can be a valid IP (v4 or v6). + bool address = 21; + + // Uuid specifies that the field must be a valid UUID as defined by + // RFC 4122 + bool uuid = 22; + + // WellKnownRegex specifies a common well known pattern defined as a regex. + KnownRegex well_known_regex = 24; + } + + // This applies to regexes HTTP_HEADER_NAME and HTTP_HEADER_VALUE to enable + // strict header validation. + // By default, this is true, and HTTP header validations are RFC-compliant. + // Setting to false will enable a looser validations that only disallows + // \r\n\0 characters, which can be used to bypass header matching rules. + optional bool strict = 25 [default = true]; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 26; +} + +// WellKnownRegex contain some well-known patterns. +enum KnownRegex { + UNKNOWN = 0; + + // HTTP header name as defined by RFC 7230. + HTTP_HEADER_NAME = 1; + + // HTTP header value as defined by RFC 7230. + HTTP_HEADER_VALUE = 2; +} + +// BytesRules describe the constraints applied to `bytes` values +message BytesRules { + // Const specifies that this field must be exactly the specified value + optional bytes const = 1; + + // Len specifies that this field must be the specified number of bytes + optional uint64 len = 13; + + // MinLen specifies that this field must be the specified number of bytes + // at a minimum + optional uint64 min_len = 2; + + // MaxLen specifies that this field must be the specified number of bytes + // at a maximum + optional uint64 max_len = 3; + + // Pattern specifes that this field must match against the specified + // regular expression (RE2 syntax). The included expression should elide + // any delimiters. + optional string pattern = 4; + + // Prefix specifies that this field must have the specified bytes at the + // beginning of the string. + optional bytes prefix = 5; + + // Suffix specifies that this field must have the specified bytes at the + // end of the string. + optional bytes suffix = 6; + + // Contains specifies that this field must have the specified bytes + // anywhere in the string. + optional bytes contains = 7; + + // In specifies that this field must be equal to one of the specified + // values + repeated bytes in = 8; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated bytes not_in = 9; + + // WellKnown rules provide advanced constraints against common byte + // patterns + oneof well_known { + // Ip specifies that the field must be a valid IP (v4 or v6) address in + // byte format + bool ip = 10; + + // Ipv4 specifies that the field must be a valid IPv4 address in byte + // format + bool ipv4 = 11; + + // Ipv6 specifies that the field must be a valid IPv6 address in byte + // format + bool ipv6 = 12; + } + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 14; +} + +// EnumRules describe the constraints applied to enum values +message EnumRules { + // Const specifies that this field must be exactly the specified value + optional int32 const = 1; + + // DefinedOnly specifies that this field must be only one of the defined + // values for this enum, failing on any undefined value. + optional bool defined_only = 2; + + // In specifies that this field must be equal to one of the specified + // values + repeated int32 in = 3; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated int32 not_in = 4; +} + +// MessageRules describe the constraints applied to embedded message values. +// For message-type fields, validation is performed recursively. +message MessageRules { + // Skip specifies that the validation rules of this field should not be + // evaluated + optional bool skip = 1; + + // Required specifies that this field must be set + optional bool required = 2; +} + +// RepeatedRules describe the constraints applied to `repeated` values +message RepeatedRules { + // MinItems specifies that this field must have the specified number of + // items at a minimum + optional uint64 min_items = 1; + + // MaxItems specifies that this field must have the specified number of + // items at a maximum + optional uint64 max_items = 2; + + // Unique specifies that all elements in this field must be unique. This + // contraint is only applicable to scalar and enum types (messages are not + // supported). + optional bool unique = 3; + + // Items specifies the contraints to be applied to each item in the field. + // Repeated message fields will still execute validation against each item + // unless skip is specified here. + optional FieldRules items = 4; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 5; +} + +// MapRules describe the constraints applied to `map` values +message MapRules { + // MinPairs specifies that this field must have the specified number of + // KVs at a minimum + optional uint64 min_pairs = 1; + + // MaxPairs specifies that this field must have the specified number of + // KVs at a maximum + optional uint64 max_pairs = 2; + + // NoSparse specifies values in this field cannot be unset. This only + // applies to map's with message value types. + optional bool no_sparse = 3; + + // Keys specifies the constraints to be applied to each key in the field. + optional FieldRules keys = 4; + + // Values specifies the constraints to be applied to the value of each key + // in the field. Message values will still have their validations evaluated + // unless skip is specified here. + optional FieldRules values = 5; + + // IgnoreEmpty specifies that the validation rules of this field should be + // evaluated only if the field is not empty + optional bool ignore_empty = 6; +} + +// AnyRules describe constraints applied exclusively to the +// `google.protobuf.Any` well-known type +message AnyRules { + // Required specifies that this field must be set + optional bool required = 1; + + // In specifies that this field's `type_url` must be equal to one of the + // specified values. + repeated string in = 2; + + // NotIn specifies that this field's `type_url` must not be equal to any of + // the specified values. + repeated string not_in = 3; +} + +// DurationRules describe the constraints applied exclusively to the +// `google.protobuf.Duration` well-known type +message DurationRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Duration const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Duration lt = 3; + + // Lt specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Duration lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Duration gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Duration gte = 6; + + // In specifies that this field must be equal to one of the specified + // values + repeated google.protobuf.Duration in = 7; + + // NotIn specifies that this field cannot be equal to one of the specified + // values + repeated google.protobuf.Duration not_in = 8; +} + +// TimestampRules describe the constraints applied exclusively to the +// `google.protobuf.Timestamp` well-known type +message TimestampRules { + // Required specifies that this field must be set + optional bool required = 1; + + // Const specifies that this field must be exactly the specified value + optional google.protobuf.Timestamp const = 2; + + // Lt specifies that this field must be less than the specified value, + // exclusive + optional google.protobuf.Timestamp lt = 3; + + // Lte specifies that this field must be less than the specified value, + // inclusive + optional google.protobuf.Timestamp lte = 4; + + // Gt specifies that this field must be greater than the specified value, + // exclusive + optional google.protobuf.Timestamp gt = 5; + + // Gte specifies that this field must be greater than the specified value, + // inclusive + optional google.protobuf.Timestamp gte = 6; + + // LtNow specifies that this must be less than the current time. LtNow + // can only be used with the Within rule. + optional bool lt_now = 7; + + // GtNow specifies that this must be greater than the current time. GtNow + // can only be used with the Within rule. + optional bool gt_now = 8; + + // Within specifies that this field must be within this duration of the + // current time. This constraint can be used alone or with the LtNow and + // GtNow rules. + optional google.protobuf.Duration within = 9; +} diff --git a/lora-ns-ttn/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto b/lora-ns-ttn/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto new file mode 100644 index 000000000..b80c85653 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto @@ -0,0 +1,144 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; +package gogoproto; + +import "google/protobuf/descriptor.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "GoGoProtos"; +option go_package = "github.com/gogo/protobuf/gogoproto"; + +extend google.protobuf.EnumOptions { + optional bool goproto_enum_prefix = 62001; + optional bool goproto_enum_stringer = 62021; + optional bool enum_stringer = 62022; + optional string enum_customname = 62023; + optional bool enumdecl = 62024; +} + +extend google.protobuf.EnumValueOptions { + optional string enumvalue_customname = 66001; +} + +extend google.protobuf.FileOptions { + optional bool goproto_getters_all = 63001; + optional bool goproto_enum_prefix_all = 63002; + optional bool goproto_stringer_all = 63003; + optional bool verbose_equal_all = 63004; + optional bool face_all = 63005; + optional bool gostring_all = 63006; + optional bool populate_all = 63007; + optional bool stringer_all = 63008; + optional bool onlyone_all = 63009; + + optional bool equal_all = 63013; + optional bool description_all = 63014; + optional bool testgen_all = 63015; + optional bool benchgen_all = 63016; + optional bool marshaler_all = 63017; + optional bool unmarshaler_all = 63018; + optional bool stable_marshaler_all = 63019; + + optional bool sizer_all = 63020; + + optional bool goproto_enum_stringer_all = 63021; + optional bool enum_stringer_all = 63022; + + optional bool unsafe_marshaler_all = 63023; + optional bool unsafe_unmarshaler_all = 63024; + + optional bool goproto_extensions_map_all = 63025; + optional bool goproto_unrecognized_all = 63026; + optional bool gogoproto_import = 63027; + optional bool protosizer_all = 63028; + optional bool compare_all = 63029; + optional bool typedecl_all = 63030; + optional bool enumdecl_all = 63031; + + optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; +} + +extend google.protobuf.MessageOptions { + optional bool goproto_getters = 64001; + optional bool goproto_stringer = 64003; + optional bool verbose_equal = 64004; + optional bool face = 64005; + optional bool gostring = 64006; + optional bool populate = 64007; + optional bool stringer = 67008; + optional bool onlyone = 64009; + + optional bool equal = 64013; + optional bool description = 64014; + optional bool testgen = 64015; + optional bool benchgen = 64016; + optional bool marshaler = 64017; + optional bool unmarshaler = 64018; + optional bool stable_marshaler = 64019; + + optional bool sizer = 64020; + + optional bool unsafe_marshaler = 64023; + optional bool unsafe_unmarshaler = 64024; + + optional bool goproto_extensions_map = 64025; + optional bool goproto_unrecognized = 64026; + + optional bool protosizer = 64028; + optional bool compare = 64029; + + optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; +} + +extend google.protobuf.FieldOptions { + optional bool nullable = 65001; + optional bool embed = 65002; + optional string customtype = 65003; + optional string customname = 65004; + optional string jsontag = 65005; + optional string moretags = 65006; + optional string casttype = 65007; + optional string castkey = 65008; + optional string castvalue = 65009; + + optional bool stdtime = 65010; + optional bool stdduration = 65011; + optional bool wktpointer = 65012; + +} diff --git a/lora-ns-ttn/src/main/proto/google/api/annotations.proto b/lora-ns-ttn/src/main/proto/google/api/annotations.proto new file mode 100644 index 000000000..efdab3db6 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright 2015 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/lora-ns-ttn/src/main/proto/google/api/http.proto b/lora-ns-ttn/src/main/proto/google/api/http.proto new file mode 100644 index 000000000..113fa936a --- /dev/null +++ b/lora-ns-ttn/src/main/proto/google/api/http.proto @@ -0,0 +1,375 @@ +// Copyright 2015 Google LLC +// +// 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. + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/_api.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/_api.proto index 0d6f9cfa3..8ed85193d 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/_api.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/_api.proto @@ -14,9 +14,14 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + // Package documentation should be added in the comment block below: // The Things Stack for LoRaWAN v3 API package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.md b/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.md index a7484487c..0e0c59e5a 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.md +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.md @@ -14,6 +14,7 @@ - [Message `GetApplicationAPIKeyRequest`](#ttn.lorawan.v3.GetApplicationAPIKeyRequest) - [Message `GetApplicationCollaboratorRequest`](#ttn.lorawan.v3.GetApplicationCollaboratorRequest) - [Message `GetApplicationRequest`](#ttn.lorawan.v3.GetApplicationRequest) + - [Message `IssueDevEUIResponse`](#ttn.lorawan.v3.IssueDevEUIResponse) - [Message `ListApplicationAPIKeysRequest`](#ttn.lorawan.v3.ListApplicationAPIKeysRequest) - [Message `ListApplicationCollaboratorsRequest`](#ttn.lorawan.v3.ListApplicationCollaboratorsRequest) - [Message `ListApplicationsRequest`](#ttn.lorawan.v3.ListApplicationsRequest) @@ -26,11 +27,32 @@ - [File `lorawan-stack/api/applicationserver.proto`](#lorawan-stack/api/applicationserver.proto) - [Message `ApplicationLink`](#ttn.lorawan.v3.ApplicationLink) - [Message `ApplicationLinkStats`](#ttn.lorawan.v3.ApplicationLinkStats) + - [Message `AsConfiguration`](#ttn.lorawan.v3.AsConfiguration) + - [Message `AsConfiguration.PubSub`](#ttn.lorawan.v3.AsConfiguration.PubSub) + - [Message `AsConfiguration.PubSub.Providers`](#ttn.lorawan.v3.AsConfiguration.PubSub.Providers) + - [Message `AsConfiguration.Webhooks`](#ttn.lorawan.v3.AsConfiguration.Webhooks) + - [Message `DecodeDownlinkRequest`](#ttn.lorawan.v3.DecodeDownlinkRequest) + - [Message `DecodeDownlinkResponse`](#ttn.lorawan.v3.DecodeDownlinkResponse) + - [Message `DecodeUplinkRequest`](#ttn.lorawan.v3.DecodeUplinkRequest) + - [Message `DecodeUplinkResponse`](#ttn.lorawan.v3.DecodeUplinkResponse) + - [Message `EncodeDownlinkRequest`](#ttn.lorawan.v3.EncodeDownlinkRequest) + - [Message `EncodeDownlinkResponse`](#ttn.lorawan.v3.EncodeDownlinkResponse) - [Message `GetApplicationLinkRequest`](#ttn.lorawan.v3.GetApplicationLinkRequest) + - [Message `GetAsConfigurationRequest`](#ttn.lorawan.v3.GetAsConfigurationRequest) + - [Message `GetAsConfigurationResponse`](#ttn.lorawan.v3.GetAsConfigurationResponse) + - [Message `NsAsHandleUplinkRequest`](#ttn.lorawan.v3.NsAsHandleUplinkRequest) - [Message `SetApplicationLinkRequest`](#ttn.lorawan.v3.SetApplicationLinkRequest) + - [Enum `AsConfiguration.PubSub.Providers.Status`](#ttn.lorawan.v3.AsConfiguration.PubSub.Providers.Status) - [Service `AppAs`](#ttn.lorawan.v3.AppAs) - [Service `As`](#ttn.lorawan.v3.As) - [Service `AsEndDeviceRegistry`](#ttn.lorawan.v3.AsEndDeviceRegistry) + - [Service `NsAs`](#ttn.lorawan.v3.NsAs) +- [File `lorawan-stack/api/applicationserver_integrations_storage.proto`](#lorawan-stack/api/applicationserver_integrations_storage.proto) + - [Message `GetStoredApplicationUpCountRequest`](#ttn.lorawan.v3.GetStoredApplicationUpCountRequest) + - [Message `GetStoredApplicationUpCountResponse`](#ttn.lorawan.v3.GetStoredApplicationUpCountResponse) + - [Message `GetStoredApplicationUpCountResponse.CountEntry`](#ttn.lorawan.v3.GetStoredApplicationUpCountResponse.CountEntry) + - [Message `GetStoredApplicationUpRequest`](#ttn.lorawan.v3.GetStoredApplicationUpRequest) + - [Service `ApplicationUpStorage`](#ttn.lorawan.v3.ApplicationUpStorage) - [File `lorawan-stack/api/applicationserver_packages.proto`](#lorawan-stack/api/applicationserver_packages.proto) - [Message `ApplicationPackage`](#ttn.lorawan.v3.ApplicationPackage) - [Message `ApplicationPackageAssociation`](#ttn.lorawan.v3.ApplicationPackageAssociation) @@ -49,7 +71,12 @@ - [Service `ApplicationPackageRegistry`](#ttn.lorawan.v3.ApplicationPackageRegistry) - [File `lorawan-stack/api/applicationserver_pubsub.proto`](#lorawan-stack/api/applicationserver_pubsub.proto) - [Message `ApplicationPubSub`](#ttn.lorawan.v3.ApplicationPubSub) + - [Message `ApplicationPubSub.AWSIoTProvider`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider) + - [Message `ApplicationPubSub.AWSIoTProvider.AccessKey`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.AccessKey) + - [Message `ApplicationPubSub.AWSIoTProvider.AssumeRole`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.AssumeRole) + - [Message `ApplicationPubSub.AWSIoTProvider.DefaultIntegration`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.DefaultIntegration) - [Message `ApplicationPubSub.MQTTProvider`](#ttn.lorawan.v3.ApplicationPubSub.MQTTProvider) + - [Message `ApplicationPubSub.MQTTProvider.HeadersEntry`](#ttn.lorawan.v3.ApplicationPubSub.MQTTProvider.HeadersEntry) - [Message `ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) - [Message `ApplicationPubSub.NATSProvider`](#ttn.lorawan.v3.ApplicationPubSub.NATSProvider) - [Message `ApplicationPubSubFormats`](#ttn.lorawan.v3.ApplicationPubSubFormats) @@ -68,6 +95,9 @@ - [Message `ApplicationWebhook.TemplateFieldsEntry`](#ttn.lorawan.v3.ApplicationWebhook.TemplateFieldsEntry) - [Message `ApplicationWebhookFormats`](#ttn.lorawan.v3.ApplicationWebhookFormats) - [Message `ApplicationWebhookFormats.FormatsEntry`](#ttn.lorawan.v3.ApplicationWebhookFormats.FormatsEntry) + - [Message `ApplicationWebhookHealth`](#ttn.lorawan.v3.ApplicationWebhookHealth) + - [Message `ApplicationWebhookHealth.WebhookHealthStatusHealthy`](#ttn.lorawan.v3.ApplicationWebhookHealth.WebhookHealthStatusHealthy) + - [Message `ApplicationWebhookHealth.WebhookHealthStatusUnhealthy`](#ttn.lorawan.v3.ApplicationWebhookHealth.WebhookHealthStatusUnhealthy) - [Message `ApplicationWebhookIdentifiers`](#ttn.lorawan.v3.ApplicationWebhookIdentifiers) - [Message `ApplicationWebhookTemplate`](#ttn.lorawan.v3.ApplicationWebhookTemplate) - [Message `ApplicationWebhookTemplate.HeadersEntry`](#ttn.lorawan.v3.ApplicationWebhookTemplate.HeadersEntry) @@ -101,7 +131,23 @@ - [Message `PeerInfo`](#ttn.lorawan.v3.PeerInfo) - [Message `PeerInfo.TagsEntry`](#ttn.lorawan.v3.PeerInfo.TagsEntry) - [File `lorawan-stack/api/configuration_services.proto`](#lorawan-stack/api/configuration_services.proto) + - [Message `BandDescription`](#ttn.lorawan.v3.BandDescription) + - [Message `BandDescription.BandDataRate`](#ttn.lorawan.v3.BandDescription.BandDataRate) + - [Message `BandDescription.Beacon`](#ttn.lorawan.v3.BandDescription.Beacon) + - [Message `BandDescription.Channel`](#ttn.lorawan.v3.BandDescription.Channel) + - [Message `BandDescription.DataRatesEntry`](#ttn.lorawan.v3.BandDescription.DataRatesEntry) + - [Message `BandDescription.DwellTime`](#ttn.lorawan.v3.BandDescription.DwellTime) + - [Message `BandDescription.Rx2Parameters`](#ttn.lorawan.v3.BandDescription.Rx2Parameters) + - [Message `BandDescription.SubBandParameters`](#ttn.lorawan.v3.BandDescription.SubBandParameters) - [Message `FrequencyPlanDescription`](#ttn.lorawan.v3.FrequencyPlanDescription) + - [Message `GetPhyVersionsRequest`](#ttn.lorawan.v3.GetPhyVersionsRequest) + - [Message `GetPhyVersionsResponse`](#ttn.lorawan.v3.GetPhyVersionsResponse) + - [Message `GetPhyVersionsResponse.VersionInfo`](#ttn.lorawan.v3.GetPhyVersionsResponse.VersionInfo) + - [Message `ListBandsRequest`](#ttn.lorawan.v3.ListBandsRequest) + - [Message `ListBandsResponse`](#ttn.lorawan.v3.ListBandsResponse) + - [Message `ListBandsResponse.DescriptionsEntry`](#ttn.lorawan.v3.ListBandsResponse.DescriptionsEntry) + - [Message `ListBandsResponse.VersionedBandDescription`](#ttn.lorawan.v3.ListBandsResponse.VersionedBandDescription) + - [Message `ListBandsResponse.VersionedBandDescription.BandEntry`](#ttn.lorawan.v3.ListBandsResponse.VersionedBandDescription.BandEntry) - [Message `ListFrequencyPlansRequest`](#ttn.lorawan.v3.ListFrequencyPlansRequest) - [Message `ListFrequencyPlansResponse`](#ttn.lorawan.v3.ListFrequencyPlansResponse) - [Service `Configuration`](#ttn.lorawan.v3.Configuration) @@ -113,24 +159,75 @@ - [Service `ContactInfoRegistry`](#ttn.lorawan.v3.ContactInfoRegistry) - [File `lorawan-stack/api/deviceclaimingserver.proto`](#lorawan-stack/api/deviceclaimingserver.proto) - [Message `AuthorizeApplicationRequest`](#ttn.lorawan.v3.AuthorizeApplicationRequest) + - [Message `AuthorizeGatewayRequest`](#ttn.lorawan.v3.AuthorizeGatewayRequest) + - [Message `CUPSRedirection`](#ttn.lorawan.v3.CUPSRedirection) + - [Message `CUPSRedirection.ClientTLS`](#ttn.lorawan.v3.CUPSRedirection.ClientTLS) - [Message `ClaimEndDeviceRequest`](#ttn.lorawan.v3.ClaimEndDeviceRequest) - [Message `ClaimEndDeviceRequest.AuthenticatedIdentifiers`](#ttn.lorawan.v3.ClaimEndDeviceRequest.AuthenticatedIdentifiers) + - [Message `ClaimGatewayRequest`](#ttn.lorawan.v3.ClaimGatewayRequest) + - [Message `ClaimGatewayRequest.AuthenticatedIdentifiers`](#ttn.lorawan.v3.ClaimGatewayRequest.AuthenticatedIdentifiers) + - [Message `GetClaimStatusResponse`](#ttn.lorawan.v3.GetClaimStatusResponse) + - [Message `GetClaimStatusResponse.VendorSpecific`](#ttn.lorawan.v3.GetClaimStatusResponse.VendorSpecific) + - [Message `GetInfoByJoinEUIRequest`](#ttn.lorawan.v3.GetInfoByJoinEUIRequest) + - [Message `GetInfoByJoinEUIResponse`](#ttn.lorawan.v3.GetInfoByJoinEUIResponse) - [Service `EndDeviceClaimingServer`](#ttn.lorawan.v3.EndDeviceClaimingServer) + - [Service `GatewayClaimingServer`](#ttn.lorawan.v3.GatewayClaimingServer) +- [File `lorawan-stack/api/devicerepository.proto`](#lorawan-stack/api/devicerepository.proto) + - [Message `DecodedMessagePayload`](#ttn.lorawan.v3.DecodedMessagePayload) + - [Message `EncodedMessagePayload`](#ttn.lorawan.v3.EncodedMessagePayload) + - [Message `EndDeviceBrand`](#ttn.lorawan.v3.EndDeviceBrand) + - [Message `EndDeviceModel`](#ttn.lorawan.v3.EndDeviceModel) + - [Message `EndDeviceModel.Battery`](#ttn.lorawan.v3.EndDeviceModel.Battery) + - [Message `EndDeviceModel.Compliances`](#ttn.lorawan.v3.EndDeviceModel.Compliances) + - [Message `EndDeviceModel.Compliances.Compliance`](#ttn.lorawan.v3.EndDeviceModel.Compliances.Compliance) + - [Message `EndDeviceModel.Dimensions`](#ttn.lorawan.v3.EndDeviceModel.Dimensions) + - [Message `EndDeviceModel.FirmwareVersion`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion) + - [Message `EndDeviceModel.FirmwareVersion.Profile`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion.Profile) + - [Message `EndDeviceModel.FirmwareVersion.ProfilesEntry`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion.ProfilesEntry) + - [Message `EndDeviceModel.HardwareVersion`](#ttn.lorawan.v3.EndDeviceModel.HardwareVersion) + - [Message `EndDeviceModel.OperatingConditions`](#ttn.lorawan.v3.EndDeviceModel.OperatingConditions) + - [Message `EndDeviceModel.OperatingConditions.Limits`](#ttn.lorawan.v3.EndDeviceModel.OperatingConditions.Limits) + - [Message `EndDeviceModel.Photos`](#ttn.lorawan.v3.EndDeviceModel.Photos) + - [Message `EndDeviceModel.Reseller`](#ttn.lorawan.v3.EndDeviceModel.Reseller) + - [Message `EndDeviceModel.Videos`](#ttn.lorawan.v3.EndDeviceModel.Videos) + - [Message `GetEndDeviceBrandRequest`](#ttn.lorawan.v3.GetEndDeviceBrandRequest) + - [Message `GetEndDeviceModelRequest`](#ttn.lorawan.v3.GetEndDeviceModelRequest) + - [Message `GetPayloadFormatterRequest`](#ttn.lorawan.v3.GetPayloadFormatterRequest) + - [Message `GetTemplateRequest`](#ttn.lorawan.v3.GetTemplateRequest) + - [Message `GetTemplateRequest.EndDeviceProfileIdentifiers`](#ttn.lorawan.v3.GetTemplateRequest.EndDeviceProfileIdentifiers) + - [Message `ListEndDeviceBrandsRequest`](#ttn.lorawan.v3.ListEndDeviceBrandsRequest) + - [Message `ListEndDeviceBrandsResponse`](#ttn.lorawan.v3.ListEndDeviceBrandsResponse) + - [Message `ListEndDeviceModelsRequest`](#ttn.lorawan.v3.ListEndDeviceModelsRequest) + - [Message `ListEndDeviceModelsResponse`](#ttn.lorawan.v3.ListEndDeviceModelsResponse) + - [Message `MessagePayloadDecoder`](#ttn.lorawan.v3.MessagePayloadDecoder) + - [Message `MessagePayloadDecoder.Example`](#ttn.lorawan.v3.MessagePayloadDecoder.Example) + - [Message `MessagePayloadEncoder`](#ttn.lorawan.v3.MessagePayloadEncoder) + - [Message `MessagePayloadEncoder.Example`](#ttn.lorawan.v3.MessagePayloadEncoder.Example) + - [Enum `KeyProvisioning`](#ttn.lorawan.v3.KeyProvisioning) + - [Enum `KeySecurity`](#ttn.lorawan.v3.KeySecurity) + - [Service `DeviceRepository`](#ttn.lorawan.v3.DeviceRepository) +- [File `lorawan-stack/api/email_messages.proto`](#lorawan-stack/api/email_messages.proto) + - [Message `CreateClientEmailMessage`](#ttn.lorawan.v3.CreateClientEmailMessage) - [File `lorawan-stack/api/end_device.proto`](#lorawan-stack/api/end_device.proto) + - [Message `ADRSettings`](#ttn.lorawan.v3.ADRSettings) + - [Message `ADRSettings.DisabledMode`](#ttn.lorawan.v3.ADRSettings.DisabledMode) + - [Message `ADRSettings.DynamicMode`](#ttn.lorawan.v3.ADRSettings.DynamicMode) + - [Message `ADRSettings.StaticMode`](#ttn.lorawan.v3.ADRSettings.StaticMode) + - [Message `BatchUpdateEndDeviceLastSeenRequest`](#ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest) + - [Message `BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate`](#ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate) + - [Message `BoolValue`](#ttn.lorawan.v3.BoolValue) - [Message `ConvertEndDeviceTemplateRequest`](#ttn.lorawan.v3.ConvertEndDeviceTemplateRequest) - [Message `CreateEndDeviceRequest`](#ttn.lorawan.v3.CreateEndDeviceRequest) + - [Message `DevAddrPrefix`](#ttn.lorawan.v3.DevAddrPrefix) - [Message `EndDevice`](#ttn.lorawan.v3.EndDevice) - [Message `EndDevice.AttributesEntry`](#ttn.lorawan.v3.EndDevice.AttributesEntry) - [Message `EndDevice.LocationsEntry`](#ttn.lorawan.v3.EndDevice.LocationsEntry) - [Message `EndDeviceAuthenticationCode`](#ttn.lorawan.v3.EndDeviceAuthenticationCode) - - [Message `EndDeviceBrand`](#ttn.lorawan.v3.EndDeviceBrand) - - [Message `EndDeviceModel`](#ttn.lorawan.v3.EndDeviceModel) - [Message `EndDeviceTemplate`](#ttn.lorawan.v3.EndDeviceTemplate) - [Message `EndDeviceTemplateFormat`](#ttn.lorawan.v3.EndDeviceTemplateFormat) - [Message `EndDeviceTemplateFormats`](#ttn.lorawan.v3.EndDeviceTemplateFormats) - [Message `EndDeviceTemplateFormats.FormatsEntry`](#ttn.lorawan.v3.EndDeviceTemplateFormats.FormatsEntry) - [Message `EndDeviceVersion`](#ttn.lorawan.v3.EndDeviceVersion) - - [Message `EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) - [Message `EndDevices`](#ttn.lorawan.v3.EndDevices) - [Message `GetEndDeviceIdentifiersForEUIsRequest`](#ttn.lorawan.v3.GetEndDeviceIdentifiersForEUIsRequest) - [Message `GetEndDeviceRequest`](#ttn.lorawan.v3.GetEndDeviceRequest) @@ -139,7 +236,20 @@ - [Message `MACParameters.Channel`](#ttn.lorawan.v3.MACParameters.Channel) - [Message `MACSettings`](#ttn.lorawan.v3.MACSettings) - [Message `MACState`](#ttn.lorawan.v3.MACState) + - [Message `MACState.DataRateRange`](#ttn.lorawan.v3.MACState.DataRateRange) + - [Message `MACState.DataRateRanges`](#ttn.lorawan.v3.MACState.DataRateRanges) + - [Message `MACState.DownlinkMessage`](#ttn.lorawan.v3.MACState.DownlinkMessage) + - [Message `MACState.DownlinkMessage.Message`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message) + - [Message `MACState.DownlinkMessage.Message.MACPayload`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload) + - [Message `MACState.DownlinkMessage.Message.MHDR`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR) - [Message `MACState.JoinAccept`](#ttn.lorawan.v3.MACState.JoinAccept) + - [Message `MACState.JoinRequest`](#ttn.lorawan.v3.MACState.JoinRequest) + - [Message `MACState.RejectedDataRateRangesEntry`](#ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry) + - [Message `MACState.UplinkMessage`](#ttn.lorawan.v3.MACState.UplinkMessage) + - [Message `MACState.UplinkMessage.RxMetadata`](#ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata) + - [Message `MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata`](#ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata) + - [Message `MACState.UplinkMessage.TxSettings`](#ttn.lorawan.v3.MACState.UplinkMessage.TxSettings) + - [Message `ResetAndGetEndDeviceRequest`](#ttn.lorawan.v3.ResetAndGetEndDeviceRequest) - [Message `Session`](#ttn.lorawan.v3.Session) - [Message `SetEndDeviceRequest`](#ttn.lorawan.v3.SetEndDeviceRequest) - [Message `UpdateEndDeviceRequest`](#ttn.lorawan.v3.UpdateEndDeviceRequest) @@ -157,6 +267,8 @@ - [Message `Event`](#ttn.lorawan.v3.Event) - [Message `Event.Authentication`](#ttn.lorawan.v3.Event.Authentication) - [Message `Event.ContextEntry`](#ttn.lorawan.v3.Event.ContextEntry) + - [Message `FindRelatedEventsRequest`](#ttn.lorawan.v3.FindRelatedEventsRequest) + - [Message `FindRelatedEventsResponse`](#ttn.lorawan.v3.FindRelatedEventsResponse) - [Message `StreamEventsRequest`](#ttn.lorawan.v3.StreamEventsRequest) - [Service `Events`](#ttn.lorawan.v3.Events) - [File `lorawan-stack/api/gateway.proto`](#lorawan-stack/api/gateway.proto) @@ -164,19 +276,21 @@ - [Message `CreateGatewayRequest`](#ttn.lorawan.v3.CreateGatewayRequest) - [Message `Gateway`](#ttn.lorawan.v3.Gateway) - [Message `Gateway.AttributesEntry`](#ttn.lorawan.v3.Gateway.AttributesEntry) + - [Message `Gateway.LRFHSS`](#ttn.lorawan.v3.Gateway.LRFHSS) - [Message `GatewayAntenna`](#ttn.lorawan.v3.GatewayAntenna) - [Message `GatewayAntenna.AttributesEntry`](#ttn.lorawan.v3.GatewayAntenna.AttributesEntry) - [Message `GatewayBrand`](#ttn.lorawan.v3.GatewayBrand) + - [Message `GatewayClaimAuthenticationCode`](#ttn.lorawan.v3.GatewayClaimAuthenticationCode) - [Message `GatewayConnectionStats`](#ttn.lorawan.v3.GatewayConnectionStats) - [Message `GatewayConnectionStats.RoundTripTimes`](#ttn.lorawan.v3.GatewayConnectionStats.RoundTripTimes) - [Message `GatewayConnectionStats.SubBand`](#ttn.lorawan.v3.GatewayConnectionStats.SubBand) - [Message `GatewayModel`](#ttn.lorawan.v3.GatewayModel) - [Message `GatewayRadio`](#ttn.lorawan.v3.GatewayRadio) - [Message `GatewayRadio.TxConfiguration`](#ttn.lorawan.v3.GatewayRadio.TxConfiguration) + - [Message `GatewayRemoteAddress`](#ttn.lorawan.v3.GatewayRemoteAddress) - [Message `GatewayStatus`](#ttn.lorawan.v3.GatewayStatus) - [Message `GatewayStatus.MetricsEntry`](#ttn.lorawan.v3.GatewayStatus.MetricsEntry) - [Message `GatewayStatus.VersionsEntry`](#ttn.lorawan.v3.GatewayStatus.VersionsEntry) - - [Message `GatewayVersion`](#ttn.lorawan.v3.GatewayVersion) - [Message `GatewayVersionIdentifiers`](#ttn.lorawan.v3.GatewayVersionIdentifiers) - [Message `Gateways`](#ttn.lorawan.v3.Gateways) - [Message `GetGatewayAPIKeyRequest`](#ttn.lorawan.v3.GetGatewayAPIKeyRequest) @@ -189,12 +303,20 @@ - [Message `SetGatewayCollaboratorRequest`](#ttn.lorawan.v3.SetGatewayCollaboratorRequest) - [Message `UpdateGatewayAPIKeyRequest`](#ttn.lorawan.v3.UpdateGatewayAPIKeyRequest) - [Message `UpdateGatewayRequest`](#ttn.lorawan.v3.UpdateGatewayRequest) + - [Enum `GatewayAntennaPlacement`](#ttn.lorawan.v3.GatewayAntennaPlacement) +- [File `lorawan-stack/api/gateway_configuration.proto`](#lorawan-stack/api/gateway_configuration.proto) + - [Message `GetGatewayConfigurationRequest`](#ttn.lorawan.v3.GetGatewayConfigurationRequest) + - [Message `GetGatewayConfigurationResponse`](#ttn.lorawan.v3.GetGatewayConfigurationResponse) + - [Service `GatewayConfigurationService`](#ttn.lorawan.v3.GatewayConfigurationService) - [File `lorawan-stack/api/gateway_services.proto`](#lorawan-stack/api/gateway_services.proto) - [Message `PullGatewayConfigurationRequest`](#ttn.lorawan.v3.PullGatewayConfigurationRequest) - [Service `GatewayAccess`](#ttn.lorawan.v3.GatewayAccess) - [Service `GatewayConfigurator`](#ttn.lorawan.v3.GatewayConfigurator) - [Service `GatewayRegistry`](#ttn.lorawan.v3.GatewayRegistry) - [File `lorawan-stack/api/gatewayserver.proto`](#lorawan-stack/api/gatewayserver.proto) + - [Message `BatchGetGatewayConnectionStatsRequest`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsRequest) + - [Message `BatchGetGatewayConnectionStatsResponse`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsResponse) + - [Message `BatchGetGatewayConnectionStatsResponse.EntriesEntry`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsResponse.EntriesEntry) - [Message `GatewayDown`](#ttn.lorawan.v3.GatewayDown) - [Message `GatewayUp`](#ttn.lorawan.v3.GatewayUp) - [Message `ScheduleDownlinkErrorDetails`](#ttn.lorawan.v3.ScheduleDownlinkErrorDetails) @@ -205,21 +327,25 @@ - [File `lorawan-stack/api/identifiers.proto`](#lorawan-stack/api/identifiers.proto) - [Message `ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) - [Message `ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) - - [Message `CombinedIdentifiers`](#ttn.lorawan.v3.CombinedIdentifiers) - [Message `EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) + - [Message `EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) - [Message `EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) - [Message `GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) + - [Message `NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) - [Message `OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) - [Message `OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) - [Message `UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) - [File `lorawan-stack/api/identityserver.proto`](#lorawan-stack/api/identityserver.proto) - [Message `AuthInfoResponse`](#ttn.lorawan.v3.AuthInfoResponse) - [Message `AuthInfoResponse.APIKeyAccess`](#ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess) + - [Message `AuthInfoResponse.GatewayToken`](#ttn.lorawan.v3.AuthInfoResponse.GatewayToken) - [Message `GetIsConfigurationRequest`](#ttn.lorawan.v3.GetIsConfigurationRequest) - [Message `GetIsConfigurationResponse`](#ttn.lorawan.v3.GetIsConfigurationResponse) - [Message `IsConfiguration`](#ttn.lorawan.v3.IsConfiguration) + - [Message `IsConfiguration.AdminRights`](#ttn.lorawan.v3.IsConfiguration.AdminRights) - [Message `IsConfiguration.EndDevicePicture`](#ttn.lorawan.v3.IsConfiguration.EndDevicePicture) - [Message `IsConfiguration.ProfilePicture`](#ttn.lorawan.v3.IsConfiguration.ProfilePicture) + - [Message `IsConfiguration.UserLogin`](#ttn.lorawan.v3.IsConfiguration.UserLogin) - [Message `IsConfiguration.UserRegistration`](#ttn.lorawan.v3.IsConfiguration.UserRegistration) - [Message `IsConfiguration.UserRegistration.AdminApproval`](#ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval) - [Message `IsConfiguration.UserRegistration.ContactInfoValidation`](#ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation) @@ -233,9 +359,13 @@ - [Message `JoinResponse`](#ttn.lorawan.v3.JoinResponse) - [File `lorawan-stack/api/joinserver.proto`](#lorawan-stack/api/joinserver.proto) - [Message `AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) + - [Message `ApplicationActivationSettings`](#ttn.lorawan.v3.ApplicationActivationSettings) - [Message `CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) - [Message `CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) + - [Message `DeleteApplicationActivationSettingsRequest`](#ttn.lorawan.v3.DeleteApplicationActivationSettingsRequest) - [Message `DeriveSessionKeysRequest`](#ttn.lorawan.v3.DeriveSessionKeysRequest) + - [Message `GetApplicationActivationSettingsRequest`](#ttn.lorawan.v3.GetApplicationActivationSettingsRequest) + - [Message `GetDefaultJoinEUIResponse`](#ttn.lorawan.v3.GetDefaultJoinEUIResponse) - [Message `GetRootKeysRequest`](#ttn.lorawan.v3.GetRootKeysRequest) - [Message `JoinAcceptMICRequest`](#ttn.lorawan.v3.JoinAcceptMICRequest) - [Message `JoinEUIPrefix`](#ttn.lorawan.v3.JoinEUIPrefix) @@ -246,6 +376,9 @@ - [Message `ProvisionEndDevicesRequest.IdentifiersList`](#ttn.lorawan.v3.ProvisionEndDevicesRequest.IdentifiersList) - [Message `ProvisionEndDevicesRequest.IdentifiersRange`](#ttn.lorawan.v3.ProvisionEndDevicesRequest.IdentifiersRange) - [Message `SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) + - [Message `SetApplicationActivationSettingsRequest`](#ttn.lorawan.v3.SetApplicationActivationSettingsRequest) + - [Service `AppJs`](#ttn.lorawan.v3.AppJs) + - [Service `ApplicationActivationSettingRegistry`](#ttn.lorawan.v3.ApplicationActivationSettingRegistry) - [Service `ApplicationCryptoService`](#ttn.lorawan.v3.ApplicationCryptoService) - [Service `AsJs`](#ttn.lorawan.v3.AsJs) - [Service `Js`](#ttn.lorawan.v3.Js) @@ -261,16 +394,21 @@ - [Message `ADRAckLimitExponentValue`](#ttn.lorawan.v3.ADRAckLimitExponentValue) - [Message `AggregatedDutyCycleValue`](#ttn.lorawan.v3.AggregatedDutyCycleValue) - [Message `CFList`](#ttn.lorawan.v3.CFList) + - [Message `ClassBCGatewayIdentifiers`](#ttn.lorawan.v3.ClassBCGatewayIdentifiers) - [Message `DLSettings`](#ttn.lorawan.v3.DLSettings) - [Message `DataRate`](#ttn.lorawan.v3.DataRate) - [Message `DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) + - [Message `DataRateOffsetValue`](#ttn.lorawan.v3.DataRateOffsetValue) + - [Message `DeviceEIRPValue`](#ttn.lorawan.v3.DeviceEIRPValue) - [Message `DownlinkPath`](#ttn.lorawan.v3.DownlinkPath) - [Message `FCtrl`](#ttn.lorawan.v3.FCtrl) - [Message `FHDR`](#ttn.lorawan.v3.FHDR) - [Message `FSKDataRate`](#ttn.lorawan.v3.FSKDataRate) + - [Message `FrequencyValue`](#ttn.lorawan.v3.FrequencyValue) - [Message `GatewayAntennaIdentifiers`](#ttn.lorawan.v3.GatewayAntennaIdentifiers) - [Message `JoinAcceptPayload`](#ttn.lorawan.v3.JoinAcceptPayload) - [Message `JoinRequestPayload`](#ttn.lorawan.v3.JoinRequestPayload) + - [Message `LRFHSSDataRate`](#ttn.lorawan.v3.LRFHSSDataRate) - [Message `LoRaDataRate`](#ttn.lorawan.v3.LoRaDataRate) - [Message `MACCommand`](#ttn.lorawan.v3.MACCommand) - [Message `MACCommand.ADRParamSetupReq`](#ttn.lorawan.v3.MACCommand.ADRParamSetupReq) @@ -303,6 +441,7 @@ - [Message `MACCommand.RxParamSetupReq`](#ttn.lorawan.v3.MACCommand.RxParamSetupReq) - [Message `MACCommand.RxTimingSetupReq`](#ttn.lorawan.v3.MACCommand.RxTimingSetupReq) - [Message `MACCommand.TxParamSetupReq`](#ttn.lorawan.v3.MACCommand.TxParamSetupReq) + - [Message `MACCommands`](#ttn.lorawan.v3.MACCommands) - [Message `MACPayload`](#ttn.lorawan.v3.MACPayload) - [Message `MHDR`](#ttn.lorawan.v3.MHDR) - [Message `Message`](#ttn.lorawan.v3.Message) @@ -313,13 +452,16 @@ - [Message `TxSettings`](#ttn.lorawan.v3.TxSettings) - [Message `TxSettings.Downlink`](#ttn.lorawan.v3.TxSettings.Downlink) - [Message `UplinkToken`](#ttn.lorawan.v3.UplinkToken) + - [Message `ZeroableFrequencyValue`](#ttn.lorawan.v3.ZeroableFrequencyValue) - [Enum `ADRAckDelayExponent`](#ttn.lorawan.v3.ADRAckDelayExponent) - [Enum `ADRAckLimitExponent`](#ttn.lorawan.v3.ADRAckLimitExponent) - [Enum `AggregatedDutyCycle`](#ttn.lorawan.v3.AggregatedDutyCycle) - [Enum `CFListType`](#ttn.lorawan.v3.CFListType) - [Enum `Class`](#ttn.lorawan.v3.Class) - [Enum `DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) + - [Enum `DataRateOffset`](#ttn.lorawan.v3.DataRateOffset) - [Enum `DeviceEIRP`](#ttn.lorawan.v3.DeviceEIRP) + - [Enum `JoinRequestType`](#ttn.lorawan.v3.JoinRequestType) - [Enum `MACCommandIdentifier`](#ttn.lorawan.v3.MACCommandIdentifier) - [Enum `MACVersion`](#ttn.lorawan.v3.MACVersion) - [Enum `MType`](#ttn.lorawan.v3.MType) @@ -329,15 +471,10 @@ - [Enum `PingSlotPeriod`](#ttn.lorawan.v3.PingSlotPeriod) - [Enum `RejoinCountExponent`](#ttn.lorawan.v3.RejoinCountExponent) - [Enum `RejoinPeriodExponent`](#ttn.lorawan.v3.RejoinPeriodExponent) + - [Enum `RejoinRequestType`](#ttn.lorawan.v3.RejoinRequestType) - [Enum `RejoinTimeExponent`](#ttn.lorawan.v3.RejoinTimeExponent) - - [Enum `RejoinType`](#ttn.lorawan.v3.RejoinType) - [Enum `RxDelay`](#ttn.lorawan.v3.RxDelay) - [Enum `TxSchedulePriority`](#ttn.lorawan.v3.TxSchedulePriority) -- [File `lorawan-stack/api/message_services.proto`](#lorawan-stack/api/message_services.proto) - - [Message `ProcessDownlinkMessageRequest`](#ttn.lorawan.v3.ProcessDownlinkMessageRequest) - - [Message `ProcessUplinkMessageRequest`](#ttn.lorawan.v3.ProcessUplinkMessageRequest) - - [Service `DownlinkMessageProcessor`](#ttn.lorawan.v3.DownlinkMessageProcessor) - - [Service `UplinkMessageProcessor`](#ttn.lorawan.v3.UplinkMessageProcessor) - [File `lorawan-stack/api/messages.proto`](#lorawan-stack/api/messages.proto) - [Message `ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) - [Message `ApplicationDownlink.ClassBC`](#ttn.lorawan.v3.ApplicationDownlink.ClassBC) @@ -350,8 +487,13 @@ - [Message `ApplicationServiceData`](#ttn.lorawan.v3.ApplicationServiceData) - [Message `ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) - [Message `ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) + - [Message `ApplicationUplink.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplink.LocationsEntry) + - [Message `ApplicationUplinkNormalized`](#ttn.lorawan.v3.ApplicationUplinkNormalized) + - [Message `ApplicationUplinkNormalized.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry) - [Message `DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) + - [Message `DownlinkQueueOperationErrorDetails`](#ttn.lorawan.v3.DownlinkQueueOperationErrorDetails) - [Message `DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) + - [Message `GatewayTxAcknowledgment`](#ttn.lorawan.v3.GatewayTxAcknowledgment) - [Message `GatewayUplinkMessage`](#ttn.lorawan.v3.GatewayUplinkMessage) - [Message `MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) - [Message `TxAcknowledgment`](#ttn.lorawan.v3.TxAcknowledgment) @@ -368,10 +510,24 @@ - [Message `MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) - [File `lorawan-stack/api/networkserver.proto`](#lorawan-stack/api/networkserver.proto) - [Message `GenerateDevAddrResponse`](#ttn.lorawan.v3.GenerateDevAddrResponse) + - [Message `GetDefaultMACSettingsRequest`](#ttn.lorawan.v3.GetDefaultMACSettingsRequest) + - [Message `GetDeviceAdressPrefixesResponse`](#ttn.lorawan.v3.GetDeviceAdressPrefixesResponse) + - [Message `GetNetIDResponse`](#ttn.lorawan.v3.GetNetIDResponse) - [Service `AsNs`](#ttn.lorawan.v3.AsNs) - [Service `GsNs`](#ttn.lorawan.v3.GsNs) - [Service `Ns`](#ttn.lorawan.v3.Ns) - [Service `NsEndDeviceRegistry`](#ttn.lorawan.v3.NsEndDeviceRegistry) +- [File `lorawan-stack/api/notification_service.proto`](#lorawan-stack/api/notification_service.proto) + - [Message `CreateNotificationRequest`](#ttn.lorawan.v3.CreateNotificationRequest) + - [Message `CreateNotificationResponse`](#ttn.lorawan.v3.CreateNotificationResponse) + - [Message `EntityStateChangedNotification`](#ttn.lorawan.v3.EntityStateChangedNotification) + - [Message `ListNotificationsRequest`](#ttn.lorawan.v3.ListNotificationsRequest) + - [Message `ListNotificationsResponse`](#ttn.lorawan.v3.ListNotificationsResponse) + - [Message `Notification`](#ttn.lorawan.v3.Notification) + - [Message `UpdateNotificationStatusRequest`](#ttn.lorawan.v3.UpdateNotificationStatusRequest) + - [Enum `NotificationReceiver`](#ttn.lorawan.v3.NotificationReceiver) + - [Enum `NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) + - [Service `NotificationService`](#ttn.lorawan.v3.NotificationService) - [File `lorawan-stack/api/oauth.proto`](#lorawan-stack/api/oauth.proto) - [Message `ListOAuthAccessTokensRequest`](#ttn.lorawan.v3.ListOAuthAccessTokensRequest) - [Message `ListOAuthClientAuthorizationsRequest`](#ttn.lorawan.v3.ListOAuthClientAuthorizationsRequest) @@ -403,8 +559,33 @@ - [Service `OrganizationAccess`](#ttn.lorawan.v3.OrganizationAccess) - [Service `OrganizationRegistry`](#ttn.lorawan.v3.OrganizationRegistry) - [File `lorawan-stack/api/packetbrokeragent.proto`](#lorawan-stack/api/packetbrokeragent.proto) + - [Message `ListForwarderRoutingPoliciesRequest`](#ttn.lorawan.v3.ListForwarderRoutingPoliciesRequest) + - [Message `ListHomeNetworkRoutingPoliciesRequest`](#ttn.lorawan.v3.ListHomeNetworkRoutingPoliciesRequest) + - [Message `ListPacketBrokerHomeNetworksRequest`](#ttn.lorawan.v3.ListPacketBrokerHomeNetworksRequest) + - [Message `ListPacketBrokerNetworksRequest`](#ttn.lorawan.v3.ListPacketBrokerNetworksRequest) + - [Message `PacketBrokerDefaultGatewayVisibility`](#ttn.lorawan.v3.PacketBrokerDefaultGatewayVisibility) + - [Message `PacketBrokerDefaultRoutingPolicy`](#ttn.lorawan.v3.PacketBrokerDefaultRoutingPolicy) + - [Message `PacketBrokerDevAddrBlock`](#ttn.lorawan.v3.PacketBrokerDevAddrBlock) + - [Message `PacketBrokerGateway`](#ttn.lorawan.v3.PacketBrokerGateway) + - [Message `PacketBrokerGateway.GatewayIdentifiers`](#ttn.lorawan.v3.PacketBrokerGateway.GatewayIdentifiers) + - [Message `PacketBrokerGatewayVisibility`](#ttn.lorawan.v3.PacketBrokerGatewayVisibility) + - [Message `PacketBrokerInfo`](#ttn.lorawan.v3.PacketBrokerInfo) + - [Message `PacketBrokerNetwork`](#ttn.lorawan.v3.PacketBrokerNetwork) + - [Message `PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) + - [Message `PacketBrokerNetworks`](#ttn.lorawan.v3.PacketBrokerNetworks) + - [Message `PacketBrokerRegisterRequest`](#ttn.lorawan.v3.PacketBrokerRegisterRequest) + - [Message `PacketBrokerRoutingPolicies`](#ttn.lorawan.v3.PacketBrokerRoutingPolicies) + - [Message `PacketBrokerRoutingPolicy`](#ttn.lorawan.v3.PacketBrokerRoutingPolicy) + - [Message `PacketBrokerRoutingPolicyDownlink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyDownlink) + - [Message `PacketBrokerRoutingPolicyUplink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyUplink) + - [Message `SetPacketBrokerDefaultGatewayVisibilityRequest`](#ttn.lorawan.v3.SetPacketBrokerDefaultGatewayVisibilityRequest) + - [Message `SetPacketBrokerDefaultRoutingPolicyRequest`](#ttn.lorawan.v3.SetPacketBrokerDefaultRoutingPolicyRequest) + - [Message `SetPacketBrokerRoutingPolicyRequest`](#ttn.lorawan.v3.SetPacketBrokerRoutingPolicyRequest) + - [Message `UpdatePacketBrokerGatewayRequest`](#ttn.lorawan.v3.UpdatePacketBrokerGatewayRequest) + - [Message `UpdatePacketBrokerGatewayResponse`](#ttn.lorawan.v3.UpdatePacketBrokerGatewayResponse) - [Service `GsPba`](#ttn.lorawan.v3.GsPba) - [Service `NsPba`](#ttn.lorawan.v3.NsPba) + - [Service `Pba`](#ttn.lorawan.v3.Pba) - [File `lorawan-stack/api/picture.proto`](#lorawan-stack/api/picture.proto) - [Message `Picture`](#ttn.lorawan.v3.Picture) - [Message `Picture.Embedded`](#ttn.lorawan.v3.Picture.Embedded) @@ -414,6 +595,8 @@ - [Message `GenerateEndDeviceQRCodeRequest.Image`](#ttn.lorawan.v3.GenerateEndDeviceQRCodeRequest.Image) - [Message `GenerateQRCodeResponse`](#ttn.lorawan.v3.GenerateQRCodeResponse) - [Message `GetQRCodeFormatRequest`](#ttn.lorawan.v3.GetQRCodeFormatRequest) + - [Message `ParseEndDeviceQRCodeRequest`](#ttn.lorawan.v3.ParseEndDeviceQRCodeRequest) + - [Message `ParseEndDeviceQRCodeResponse`](#ttn.lorawan.v3.ParseEndDeviceQRCodeResponse) - [Message `QRCodeFormat`](#ttn.lorawan.v3.QRCodeFormat) - [Message `QRCodeFormats`](#ttn.lorawan.v3.QRCodeFormats) - [Message `QRCodeFormats.FormatsEntry`](#ttn.lorawan.v3.QRCodeFormats.FormatsEntry) @@ -433,13 +616,27 @@ - [Message `Rights`](#ttn.lorawan.v3.Rights) - [Enum `Right`](#ttn.lorawan.v3.Right) - [File `lorawan-stack/api/search_services.proto`](#lorawan-stack/api/search_services.proto) + - [Message `SearchAccountsRequest`](#ttn.lorawan.v3.SearchAccountsRequest) + - [Message `SearchAccountsResponse`](#ttn.lorawan.v3.SearchAccountsResponse) + - [Message `SearchApplicationsRequest`](#ttn.lorawan.v3.SearchApplicationsRequest) + - [Message `SearchApplicationsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchApplicationsRequest.AttributesContainEntry) + - [Message `SearchClientsRequest`](#ttn.lorawan.v3.SearchClientsRequest) + - [Message `SearchClientsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchClientsRequest.AttributesContainEntry) - [Message `SearchEndDevicesRequest`](#ttn.lorawan.v3.SearchEndDevicesRequest) - [Message `SearchEndDevicesRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchEndDevicesRequest.AttributesContainEntry) - - [Message `SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) - - [Message `SearchEntitiesRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchEntitiesRequest.AttributesContainEntry) + - [Message `SearchGatewaysRequest`](#ttn.lorawan.v3.SearchGatewaysRequest) + - [Message `SearchGatewaysRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchGatewaysRequest.AttributesContainEntry) + - [Message `SearchOrganizationsRequest`](#ttn.lorawan.v3.SearchOrganizationsRequest) + - [Message `SearchOrganizationsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchOrganizationsRequest.AttributesContainEntry) + - [Message `SearchUsersRequest`](#ttn.lorawan.v3.SearchUsersRequest) + - [Message `SearchUsersRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchUsersRequest.AttributesContainEntry) - [Service `EndDeviceRegistrySearch`](#ttn.lorawan.v3.EndDeviceRegistrySearch) - [Service `EntityRegistrySearch`](#ttn.lorawan.v3.EntityRegistrySearch) +- [File `lorawan-stack/api/secrets.proto`](#lorawan-stack/api/secrets.proto) + - [Message `Secret`](#ttn.lorawan.v3.Secret) - [File `lorawan-stack/api/user.proto`](#lorawan-stack/api/user.proto) + - [Message `CreateLoginTokenRequest`](#ttn.lorawan.v3.CreateLoginTokenRequest) + - [Message `CreateLoginTokenResponse`](#ttn.lorawan.v3.CreateLoginTokenResponse) - [Message `CreateTemporaryPasswordRequest`](#ttn.lorawan.v3.CreateTemporaryPasswordRequest) - [Message `CreateUserAPIKeyRequest`](#ttn.lorawan.v3.CreateUserAPIKeyRequest) - [Message `CreateUserRequest`](#ttn.lorawan.v3.CreateUserRequest) @@ -452,6 +649,7 @@ - [Message `ListUserAPIKeysRequest`](#ttn.lorawan.v3.ListUserAPIKeysRequest) - [Message `ListUserSessionsRequest`](#ttn.lorawan.v3.ListUserSessionsRequest) - [Message `ListUsersRequest`](#ttn.lorawan.v3.ListUsersRequest) + - [Message `LoginToken`](#ttn.lorawan.v3.LoginToken) - [Message `SendInvitationRequest`](#ttn.lorawan.v3.SendInvitationRequest) - [Message `UpdateUserAPIKeyRequest`](#ttn.lorawan.v3.UpdateUserAPIKeyRequest) - [Message `UpdateUserPasswordRequest`](#ttn.lorawan.v3.UpdateUserPasswordRequest) @@ -479,13 +677,26 @@ Application is the message that defines an Application in the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `name` | [`string`](#string) | | | -| `description` | [`string`](#string) | | | +| `ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | The identifiers of the application. These are public and can be seen by any authenticated user in the network. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the application was created. This information is public and can be seen by any authenticated user in the network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the application was last updated. This information is public and can be seen by any authenticated user in the network. | +| `deleted_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the application was deleted. This information is public and can be seen by any authenticated user in the network. | +| `name` | [`string`](#string) | | The name of the application. | +| `description` | [`string`](#string) | | A description for the application. | | `attributes` | [`Application.AttributesEntry`](#ttn.lorawan.v3.Application.AttributesEntry) | repeated | Key-value attributes for this application. Typically used for organizing applications or for storing integration-specific data. | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application. This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `technical_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `network_server_address` | [`string`](#string) | | The address of the Network Server where this application is supposed to be registered. If set, this fields indicates where end devices for this application should be registered. + +Stored in Entity Registry. The typical format of the address is "host:port". If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. The connection shall be established with transport layer security (TLS). Custom certificate authorities may be configured out-of-band. | +| `application_server_address` | [`string`](#string) | | The address of the Application Server where this application is supposed to be registered. If set, this fields indicates where end devices for this application should be registered. + +Stored in Entity Registry. The typical format of the address is "host:port". If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. The connection shall be established with transport layer security (TLS). Custom certificate authorities may be configured out-of-band. | +| `join_server_address` | [`string`](#string) | | The address of the Join Server where this application is supposed to be registered. If set, this fields indicates where end devices for this application should be registered. + +Stored in Entity Registry. The typical format of the address is "host:port". If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. The connection shall be established with transport layer security (TLS). Custom certificate authorities may be configured out-of-band. | +| `dev_eui_counter` | [`uint32`](#uint32) | | | #### Field Rules @@ -494,7 +705,11 @@ Application is the message that defines an Application in the network. | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| +| `contact_info` |

`repeated.max_items`: `10`

| +| `network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `application_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `join_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| ### Message `Application.AttributesEntry` @@ -516,6 +731,7 @@ Application is the message that defines an Application in the network. | `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | | `name` | [`string`](#string) | | | | `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | #### Field Rules @@ -523,7 +739,8 @@ Application is the message that defines an Application in the network. | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| -| `rights` |

`repeated.items.enum.defined_only`: `true`

| +| `rights` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `expires_at` |

`timestamp.gt_now`: `true`

| ### Message `CreateApplicationRequest` @@ -579,11 +796,24 @@ Application is the message that defines an Application in the network. | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| +### Message `IssueDevEUIResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dev_eui` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `dev_eui` |

`bytes.len`: `8`

| + ### Message `ListApplicationAPIKeysRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `order` | [`string`](#string) | | Order the results by this field path. Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | @@ -592,6 +822,7 @@ Application is the message that defines an Application in the network. | Field | Validations | | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| +| `order` |

`string.in`: `[ api_key_id -api_key_id name -name created_at -created_at expires_at -expires_at]`

| | `limit` |

`uint32.lte`: `1000`

| ### Message `ListApplicationCollaboratorsRequest` @@ -601,6 +832,7 @@ Application is the message that defines an Application in the network. | `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | #### Field Rules @@ -608,6 +840,7 @@ Application is the message that defines an Application in the network. | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| | `limit` |

`uint32.lte`: `1000`

| +| `order` |

`string.in`: `[ id -id -rights rights]`

| ### Message `ListApplicationsRequest` @@ -618,6 +851,7 @@ Application is the message that defines an Application in the network. | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted applications. | #### Field Rules @@ -646,6 +880,7 @@ Application is the message that defines an Application in the network. | ----- | ---- | ----- | ----------- | | `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | | `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the api key fields that should be updated. | #### Field Rules @@ -671,6 +906,9 @@ Application is the message that defines an Application in the network. ### Service `ApplicationAccess` +The ApplicationAcces service, exposed by the Identity Server, is used to manage +API keys and collaborators of applications. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `ListRights` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this application. | @@ -699,6 +937,9 @@ Application is the message that defines an Application in the network. ### Service `ApplicationRegistry` +The ApplicationRegistry service, exposed by the Identity Server, is used to manage +application registrations. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `Create` | [`CreateApplicationRequest`](#ttn.lorawan.v3.CreateApplicationRequest) | [`Application`](#ttn.lorawan.v3.Application) | Create a new application. This also sets the given organization or user as first collaborator with all possible rights. | @@ -706,6 +947,11 @@ Application is the message that defines an Application in the network. | `List` | [`ListApplicationsRequest`](#ttn.lorawan.v3.ListApplicationsRequest) | [`Applications`](#ttn.lorawan.v3.Applications) | List applications where the given user or organization is a direct collaborator. If no user or organization is given, this returns the applications the caller has access to. Similar to Get, this selects the fields given by the field mask. More or less fields may be returned, depending on the rights of the caller. | | `Update` | [`UpdateApplicationRequest`](#ttn.lorawan.v3.UpdateApplicationRequest) | [`Application`](#ttn.lorawan.v3.Application) | Update the application, changing the fields specified by the field mask to the provided values. | | `Delete` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the application. This may not release the application ID for reuse. All end devices must be deleted from the application before it can be deleted. | +| `Restore` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Restore a recently deleted application. + +Deployment configuration may specify if, and for how long after deletion, entities can be restored. | +| `Purge` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Purge the application. This will release the application ID for reuse. All end devices must be deleted from the application before it can be deleted. The application owner is responsible for clearing data from any (external) integrations that may store and expose data by application ID | +| `IssueDevEUI` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`IssueDevEUIResponse`](#ttn.lorawan.v3.IssueDevEUIResponse) | Request DevEUI from the configured address block for a device inside the application. The maximum number of DevEUI's issued per application can be configured. | #### HTTP bindings @@ -719,6 +965,9 @@ Application is the message that defines an Application in the network. | `List` | `GET` | `/api/v3/organizations/{collaborator.organization_ids.organization_id}/applications` | | | `Update` | `PUT` | `/api/v3/applications/{application.ids.application_id}` | `*` | | `Delete` | `DELETE` | `/api/v3/applications/{application_id}` | | +| `Restore` | `POST` | `/api/v3/applications/{application_id}/restore` | | +| `Purge` | `DELETE` | `/api/v3/applications/{application_id}/purge` | | +| `IssueDevEUI` | `POST` | `/api/v3/applications/{application_id}/dev-eui` | | ## File `lorawan-stack/api/applicationserver.proto` @@ -726,19 +975,9 @@ Application is the message that defines an Application in the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `network_server_address` | [`string`](#string) | | The address of the external Network Server where to link to. The typical format of the address is "host:port". If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. Leave empty when linking to a cluster Network Server. | -| `api_key` | [`string`](#string) | | | -| `default_formatters` | [`MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) | | | -| `tls` | [`bool`](#bool) | | Enable TLS for linking to the external Network Server. For cluster-local Network Servers, the cluster's TLS setting is used. | +| `default_formatters` | [`MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) | | Default message payload formatters to use when there are no formatters defined on the end device level. | | `skip_payload_crypto` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Skip decryption of uplink payloads and encryption of downlink payloads. Leave empty for the using the Application Server's default setting. | -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| -| `api_key` |

`string.min_len`: `1`

| - ### Message `ApplicationLinkStats` Link stats as monitored by the Application Server. @@ -758,6 +997,107 @@ Link stats as monitored by the Application Server. | ----- | ----------- | | `network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +### Message `AsConfiguration` + +Application Server configuration. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `pubsub` | [`AsConfiguration.PubSub`](#ttn.lorawan.v3.AsConfiguration.PubSub) | | | +| `webhooks` | [`AsConfiguration.Webhooks`](#ttn.lorawan.v3.AsConfiguration.Webhooks) | | | + +### Message `AsConfiguration.PubSub` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `providers` | [`AsConfiguration.PubSub.Providers`](#ttn.lorawan.v3.AsConfiguration.PubSub.Providers) | | | + +### Message `AsConfiguration.PubSub.Providers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `mqtt` | [`AsConfiguration.PubSub.Providers.Status`](#ttn.lorawan.v3.AsConfiguration.PubSub.Providers.Status) | | | +| `nats` | [`AsConfiguration.PubSub.Providers.Status`](#ttn.lorawan.v3.AsConfiguration.PubSub.Providers.Status) | | | + +### Message `AsConfiguration.Webhooks` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `unhealthy_attempts_threshold` | [`int64`](#int64) | | | +| `unhealthy_retry_interval` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | + +### Message `DecodeDownlinkRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | | +| `downlink` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | +| `formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | | +| `parameter` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| +| `downlink` |

`message.required`: `true`

| +| `formatter` |

`enum.defined_only`: `true`

| + +### Message `DecodeDownlinkResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `downlink` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | + +### Message `DecodeUplinkRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | | +| `uplink` | [`ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) | | | +| `formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | | +| `parameter` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| +| `uplink` |

`message.required`: `true`

| +| `formatter` |

`enum.defined_only`: `true`

| + +### Message `DecodeUplinkResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `uplink` | [`ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) | | | + +### Message `EncodeDownlinkRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | | +| `downlink` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | +| `formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | | +| `parameter` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| +| `downlink` |

`message.required`: `true`

| +| `formatter` |

`enum.defined_only`: `true`

| + +### Message `EncodeDownlinkResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `downlink` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | + ### Message `GetApplicationLinkRequest` | Field | Type | Label | Description | @@ -771,6 +1111,28 @@ Link stats as monitored by the Application Server. | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| +### Message `GetAsConfigurationRequest` + +### Message `GetAsConfigurationResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `configuration` | [`AsConfiguration`](#ttn.lorawan.v3.AsConfiguration) | | | + +### Message `NsAsHandleUplinkRequest` + +Container for multiple Application uplink messages. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ups` | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ups` |

`repeated.min_items`: `1`

| + ### Message `SetApplicationLinkRequest` | Field | Type | Label | Description | @@ -786,18 +1148,29 @@ Link stats as monitored by the Application Server. | `application_ids` |

`message.required`: `true`

| | `link` |

`message.required`: `true`

| +### Enum `AsConfiguration.PubSub.Providers.Status` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `ENABLED` | 0 | No restrictions are in place. | +| `WARNING` | 1 | Warnings are being emitted that the provider will be deprecated in the future. | +| `DISABLED` | 2 | New integrations cannot be set up, and old ones do not start. | + ### Service `AppAs` The AppAs service connects an application or integration to an Application Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `Subscribe` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) _stream_ | | -| `DownlinkQueuePush` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `DownlinkQueueReplace` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `DownlinkQueueList` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`ApplicationDownlinks`](#ttn.lorawan.v3.ApplicationDownlinks) | | -| `GetMQTTConnectionInfo` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | | -| `SimulateUplink` | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| `Subscribe` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) _stream_ | Subscribe to upstream messages. | +| `DownlinkQueuePush` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Push downlink messages to the end of the downlink queue. | +| `DownlinkQueueReplace` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Replace the entire downlink queue with the specified messages. This can also be used to empty the queue by specifying no messages. | +| `DownlinkQueueList` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`ApplicationDownlinks`](#ttn.lorawan.v3.ApplicationDownlinks) | List the items currently in the downlink queue. | +| `GetMQTTConnectionInfo` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | Get connection information to connect an MQTT client. | +| `SimulateUplink` | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Simulate an upstream message. This can be used to test integrations. | +| `EncodeDownlink` | [`EncodeDownlinkRequest`](#ttn.lorawan.v3.EncodeDownlinkRequest) | [`EncodeDownlinkResponse`](#ttn.lorawan.v3.EncodeDownlinkResponse) | | +| `DecodeUplink` | [`DecodeUplinkRequest`](#ttn.lorawan.v3.DecodeUplinkRequest) | [`DecodeUplinkResponse`](#ttn.lorawan.v3.DecodeUplinkResponse) | | +| `DecodeDownlink` | [`DecodeDownlinkRequest`](#ttn.lorawan.v3.DecodeDownlinkRequest) | [`DecodeDownlinkResponse`](#ttn.lorawan.v3.DecodeDownlinkResponse) | | #### HTTP bindings @@ -808,6 +1181,9 @@ The AppAs service connects an application or integration to an Application Serve | `DownlinkQueueList` | `GET` | `/api/v3/as/applications/{application_ids.application_id}/devices/{device_id}/down` | | | `GetMQTTConnectionInfo` | `GET` | `/api/v3/as/applications/{application_id}/mqtt-connection-info` | | | `SimulateUplink` | `POST` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/simulate` | `*` | +| `EncodeDownlink` | `POST` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/encode` | `*` | +| `DecodeUplink` | `POST` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/decode` | `*` | +| `DecodeDownlink` | `POST` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/decode` | `*` | ### Service `As` @@ -815,10 +1191,11 @@ The As service manages the Application Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `GetLink` | [`GetApplicationLinkRequest`](#ttn.lorawan.v3.GetApplicationLinkRequest) | [`ApplicationLink`](#ttn.lorawan.v3.ApplicationLink) | | -| `SetLink` | [`SetApplicationLinkRequest`](#ttn.lorawan.v3.SetApplicationLinkRequest) | [`ApplicationLink`](#ttn.lorawan.v3.ApplicationLink) | Set a link configuration from the Application Server a Network Server. This call returns immediately after setting the link configuration; it does not wait for a link to establish. To get link statistics or errors, use the `GetLinkStats` call. | -| `DeleteLink` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| `GetLink` | [`GetApplicationLinkRequest`](#ttn.lorawan.v3.GetApplicationLinkRequest) | [`ApplicationLink`](#ttn.lorawan.v3.ApplicationLink) | Get a link configuration from the Application Server to Network Server. This only contains the configuration. Use GetLinkStats to view statistics and any link errors. | +| `SetLink` | [`SetApplicationLinkRequest`](#ttn.lorawan.v3.SetApplicationLinkRequest) | [`ApplicationLink`](#ttn.lorawan.v3.ApplicationLink) | Set a link configuration from the Application Server a Network Server. This call returns immediately after setting the link configuration; it does not wait for a link to establish. To get link statistics or errors, use GetLinkStats. Note that there can only be one Application Server instance linked to a Network Server for a given application at a time. | +| `DeleteLink` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the link between the Application Server and Network Server for the specified application. | | `GetLinkStats` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`ApplicationLinkStats`](#ttn.lorawan.v3.ApplicationLinkStats) | GetLinkStats returns the link statistics. This call returns a NotFound error code if there is no link for the given application identifiers. This call returns the error code of the link error if linking to a Network Server failed. | +| `GetConfiguration` | [`GetAsConfigurationRequest`](#ttn.lorawan.v3.GetAsConfigurationRequest) | [`GetAsConfigurationResponse`](#ttn.lorawan.v3.GetAsConfigurationResponse) | | #### HTTP bindings @@ -828,6 +1205,7 @@ The As service manages the Application Server. | `SetLink` | `PUT` | `/api/v3/as/applications/{application_ids.application_id}/link` | `*` | | `DeleteLink` | `DELETE` | `/api/v3/as/applications/{application_id}/link` | | | `GetLinkStats` | `GET` | `/api/v3/as/applications/{application_id}/link/stats` | | +| `GetConfiguration` | `GET` | `/api/v3/as/configuration` | | ### Service `AsEndDeviceRegistry` @@ -848,6 +1226,87 @@ The AsEndDeviceRegistry service allows clients to manage their end devices on th | `Set` | `POST` | `/api/v3/as/applications/{end_device.ids.application_ids.application_id}/devices` | `*` | | `Delete` | `DELETE` | `/api/v3/as/applications/{application_ids.application_id}/devices/{device_id}` | | +### Service `NsAs` + +The NsAs service connects a Network Server to an Application Server. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `HandleUplink` | [`NsAsHandleUplinkRequest`](#ttn.lorawan.v3.NsAsHandleUplinkRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Handle Application uplink messages. | + +## File `lorawan-stack/api/applicationserver_integrations_storage.proto` + +### Message `GetStoredApplicationUpCountRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Count upstream messages from all end devices of an application. Cannot be used in conjunction with end_device_ids. | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | Count upstream messages from a single end device. Cannot be used in conjunction with application_ids. | +| `type` | [`string`](#string) | | Count upstream messages of a specific type. If not set, then all upstream messages are returned. | +| `after` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Count upstream messages after this timestamp only. Cannot be used in conjunction with last. | +| `before` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Count upstream messages before this timestamp only. Cannot be used in conjunction with last. | +| `f_port` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Count uplinks on a specific FPort only. | +| `last` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Count upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `type` |

`string.in`: `[ uplink_message join_accept downlink_ack downlink_nack downlink_sent downlink_failed downlink_queued downlink_queue_invalidated location_solved service_data]`

| + +### Message `GetStoredApplicationUpCountResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `count` | [`GetStoredApplicationUpCountResponse.CountEntry`](#ttn.lorawan.v3.GetStoredApplicationUpCountResponse.CountEntry) | repeated | Number of stored messages by end device ID. | + +### Message `GetStoredApplicationUpCountResponse.CountEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`uint32`](#uint32) | | | + +### Message `GetStoredApplicationUpRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Query upstream messages from all end devices of an application. Cannot be used in conjunction with end_device_ids. | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | Query upstream messages from a single end device. Cannot be used in conjunction with application_ids. | +| `type` | [`string`](#string) | | Query upstream messages of a specific type. If not set, then all upstream messages are returned. | +| `limit` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Limit number of results. | +| `after` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Query upstream messages after this timestamp only. Cannot be used in conjunction with last. | +| `before` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Query upstream messages before this timestamp only. Cannot be used in conjunction with last. | +| `f_port` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Query uplinks on a specific FPort only. | +| `order` | [`string`](#string) | | Order results. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the upstream message fields that should be returned. See the API reference for allowed field names for each type of upstream message. | +| `last` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Query upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `type` |

`string.in`: `[ uplink_message uplink_normalized join_accept downlink_ack downlink_nack downlink_sent downlink_failed downlink_queued downlink_queue_invalidated location_solved service_data]`

| +| `order` |

`string.in`: `[ -received_at received_at]`

| + +### Service `ApplicationUpStorage` + +The ApplicationUpStorage service can be used to query stored application upstream messages. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `GetStoredApplicationUp` | [`GetStoredApplicationUpRequest`](#ttn.lorawan.v3.GetStoredApplicationUpRequest) | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) _stream_ | Returns a stream of application messages that have been stored in the database. | +| `GetStoredApplicationUpCount` | [`GetStoredApplicationUpCountRequest`](#ttn.lorawan.v3.GetStoredApplicationUpCountRequest) | [`GetStoredApplicationUpCountResponse`](#ttn.lorawan.v3.GetStoredApplicationUpCountResponse) | Returns how many application messages have been stored in the database for an application or end device. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `GetStoredApplicationUp` | `GET` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}` | | +| `GetStoredApplicationUp` | `GET` | `/api/v3/as/applications/{application_ids.application_id}/packages/storage/{type}` | | +| `GetStoredApplicationUpCount` | `GET` | `/api/v3/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}/count` | | +| `GetStoredApplicationUpCount` | `GET` | `/api/v3/as/applications/{application_ids.application_id}/packages/storage/{type}/count` | | + ## File `lorawan-stack/api/applicationserver_packages.proto` ### Message `ApplicationPackage` @@ -1068,16 +1527,19 @@ The AsEndDeviceRegistry service allows clients to manage their end devices on th | `format` | [`string`](#string) | | The format to use for the body. Supported values depend on the Application Server configuration. | | `nats` | [`ApplicationPubSub.NATSProvider`](#ttn.lorawan.v3.ApplicationPubSub.NATSProvider) | | | | `mqtt` | [`ApplicationPubSub.MQTTProvider`](#ttn.lorawan.v3.ApplicationPubSub.MQTTProvider) | | | +| `aws_iot` | [`ApplicationPubSub.AWSIoTProvider`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider) | | | | `base_topic` | [`string`](#string) | | Base topic name to which the messages topic is appended. | | `downlink_push` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | The topic to which the Application Server subscribes for downlink queue push operations. | | `downlink_replace` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | The topic to which the Application Server subscribes for downlink queue replace operations. | | `uplink_message` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | +| `uplink_normalized` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `join_accept` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `downlink_ack` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `downlink_nack` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `downlink_sent` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `downlink_failed` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `downlink_queued` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | +| `downlink_queue_invalidated` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `location_solved` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | | `service_data` | [`ApplicationPubSub.Message`](#ttn.lorawan.v3.ApplicationPubSub.Message) | | | @@ -1089,6 +1551,66 @@ The AsEndDeviceRegistry service allows clients to manage their end devices on th | `format` |

`string.max_len`: `20`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| | `base_topic` |

`string.max_len`: `100`

| +### Message `ApplicationPubSub.AWSIoTProvider` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `region` | [`string`](#string) | | The AWS region. | +| `access_key` | [`ApplicationPubSub.AWSIoTProvider.AccessKey`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.AccessKey) | | If set, the integration will use an AWS access key. | +| `assume_role` | [`ApplicationPubSub.AWSIoTProvider.AssumeRole`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.AssumeRole) | | If set, the integration will assume the given role during operation. | +| `endpoint_address` | [`string`](#string) | | The endpoint address to connect to. If the endpoint address is left empty, the integration will try to discover it. | +| `default` | [`ApplicationPubSub.AWSIoTProvider.DefaultIntegration`](#ttn.lorawan.v3.ApplicationPubSub.AWSIoTProvider.DefaultIntegration) | | Enable the default integration. This overrides custom base topic and message topics of the pub/sub integration. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `region` |

`string.in`: `[af-south-1 ap-east-1 ap-northeast-1 ap-northeast-2 ap-south-1 ap-southeast-1 ap-southeast-2 ca-central-1 eu-central-1 eu-north-1 eu-south-1 eu-west-1 eu-west-2 eu-west-3 me-south-1 sa-east-1 us-east-1 us-east-2 us-west-1 us-west-2]`

| +| `endpoint_address` |

`string.max_len`: `128`

`string.pattern`: `^((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])|)$`

| + +### Message `ApplicationPubSub.AWSIoTProvider.AccessKey` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `access_key_id` | [`string`](#string) | | | +| `secret_access_key` | [`string`](#string) | | | +| `session_token` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `access_key_id` |

`string.min_len`: `16`

`string.max_len`: `128`

`string.pattern`: `^[\w]*$`

| +| `secret_access_key` |

`string.max_len`: `40`

| +| `session_token` |

`string.max_len`: `256`

| + +### Message `ApplicationPubSub.AWSIoTProvider.AssumeRole` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `arn` | [`string`](#string) | | | +| `external_id` | [`string`](#string) | | | +| `session_duration` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `arn` |

`string.pattern`: `^arn:aws:iam::[0-9]{12}:role\/[A-Za-z0-9_+=,.@-]+$`

| +| `external_id` |

`string.max_len`: `1224`

`string.pattern`: `^[\w+=,.@:\/-]*$`

| + +### Message `ApplicationPubSub.AWSIoTProvider.DefaultIntegration` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `stack_name` | [`string`](#string) | | The stack name that is associated with the CloudFormation deployment of The Things Stack Enterprise integration. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `stack_name` |

`string.max_len`: `128`

`string.pattern`: `^[A-Za-z][A-Za-z0-9\-]*$`

| + ### Message `ApplicationPubSub.MQTTProvider` The MQTT provider settings. @@ -1105,6 +1627,7 @@ The MQTT provider settings. | `tls_ca` | [`bytes`](#bytes) | | The server Root CA certificate. PEM formatted. | | `tls_client_cert` | [`bytes`](#bytes) | | The client certificate. PEM formatted. | | `tls_client_key` | [`bytes`](#bytes) | | The client private key. PEM formatted. | +| `headers` | [`ApplicationPubSub.MQTTProvider.HeadersEntry`](#ttn.lorawan.v3.ApplicationPubSub.MQTTProvider.HeadersEntry) | repeated | HTTP headers to use on MQTT-over-Websocket connections. | #### Field Rules @@ -1114,6 +1637,16 @@ The MQTT provider settings. | `client_id` |

`string.max_len`: `23`

| | `username` |

`string.max_len`: `100`

| | `password` |

`string.max_len`: `100`

| +| `tls_ca` |

`bytes.max_len`: `8192`

| +| `tls_client_cert` |

`bytes.max_len`: `8192`

| +| `tls_client_key` |

`bytes.max_len`: `8192`

| + +### Message `ApplicationPubSub.MQTTProvider.HeadersEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | ### Message `ApplicationPubSub.Message` @@ -1258,14 +1791,18 @@ The NATS provider settings. | `template_fields` | [`ApplicationWebhook.TemplateFieldsEntry`](#ttn.lorawan.v3.ApplicationWebhook.TemplateFieldsEntry) | repeated | The value of the fields used by the template. Maps field.id to the value. | | `downlink_api_key` | [`string`](#string) | | The API key to be used for downlink queue operations. The field is provided for convenience reasons, and can contain API keys with additional rights (albeit this is discouraged). | | `uplink_message` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | +| `uplink_normalized` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `join_accept` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `downlink_ack` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `downlink_nack` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `downlink_sent` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `downlink_failed` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `downlink_queued` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | +| `downlink_queue_invalidated` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `location_solved` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | | `service_data` | [`ApplicationWebhook.Message`](#ttn.lorawan.v3.ApplicationWebhook.Message) | | | +| `health_status` | [`ApplicationWebhookHealth`](#ttn.lorawan.v3.ApplicationWebhookHealth) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | #### Field Rules @@ -1273,7 +1810,9 @@ The NATS provider settings. | ----- | ----------- | | `ids` |

`message.required`: `true`

| | `base_url` |

`string.uri`: `true`

| +| `headers` |

`map.max_pairs`: `50`

`map.keys.string.max_len`: `64`

`map.values.string.max_len`: `4096`

| | `format` |

`string.max_len`: `20`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `downlink_api_key` |

`string.max_len`: `128`

| ### Message `ApplicationWebhook.HeadersEntry` @@ -1288,6 +1827,12 @@ The NATS provider settings. | ----- | ---- | ----- | ----------- | | `path` | [`string`](#string) | | Path to append to the base URL. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `path` |

`string.max_len`: `64`

| + ### Message `ApplicationWebhook.TemplateFieldsEntry` | Field | Type | Label | Description | @@ -1308,6 +1853,29 @@ The NATS provider settings. | `key` | [`string`](#string) | | | | `value` | [`string`](#string) | | | +### Message `ApplicationWebhookHealth` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `healthy` | [`ApplicationWebhookHealth.WebhookHealthStatusHealthy`](#ttn.lorawan.v3.ApplicationWebhookHealth.WebhookHealthStatusHealthy) | | | +| `unhealthy` | [`ApplicationWebhookHealth.WebhookHealthStatusUnhealthy`](#ttn.lorawan.v3.ApplicationWebhookHealth.WebhookHealthStatusUnhealthy) | | | + +### Message `ApplicationWebhookHealth.WebhookHealthStatusHealthy` + +### Message `ApplicationWebhookHealth.WebhookHealthStatusUnhealthy` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `failed_attempts` | [`uint64`](#uint64) | | | +| `last_failed_attempt_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `last_failed_attempt_details` | [`ErrorDetails`](#ttn.lorawan.v3.ErrorDetails) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `last_failed_attempt_at` |

`timestamp.required`: `true`

| + ### Message `ApplicationWebhookIdentifiers` | Field | Type | Label | Description | @@ -1338,14 +1906,17 @@ The NATS provider settings. | `fields` | [`ApplicationWebhookTemplateField`](#ttn.lorawan.v3.ApplicationWebhookTemplateField) | repeated | | | `create_downlink_api_key` | [`bool`](#bool) | | Control the creation of the downlink queue operations API key. | | `uplink_message` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | +| `uplink_normalized` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `join_accept` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `downlink_ack` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `downlink_nack` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `downlink_sent` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `downlink_failed` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `downlink_queued` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | +| `downlink_queue_invalidated` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `location_solved` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | | `service_data` | [`ApplicationWebhookTemplate.Message`](#ttn.lorawan.v3.ApplicationWebhookTemplate.Message) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | #### Field Rules @@ -1358,6 +1929,7 @@ The NATS provider settings. | `info_url` |

`string.uri`: `true`

| | `documentation_url` |

`string.uri`: `true`

| | `base_url` |

`string.uri`: `true`

| +| `headers` |

`map.max_pairs`: `50`

`map.keys.string.max_len`: `64`

`map.values.string.max_len`: `256`

| | `format` |

`string.max_len`: `20`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| ### Message `ApplicationWebhookTemplate.HeadersEntry` @@ -1373,6 +1945,12 @@ The NATS provider settings. | ----- | ---- | ----- | ----------- | | `path` | [`string`](#string) | | Path to append to the base URL. Can contain template fields, in RFC 6570 format. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `path` |

`string.max_len`: `64`

| + ### Message `ApplicationWebhookTemplateField` ApplicationWebhookTemplateField represents a custom field that needs to be filled by the user in order to use the template. @@ -1386,6 +1964,7 @@ The fields are meant to be replaced inside the URLs and headers when the webhook | `description` | [`string`](#string) | | | | `secret` | [`bool`](#bool) | | Secret decides if the field should be shown in plain-text or should stay hidden. | | `default_value` | [`string`](#string) | | | +| `optional` | [`bool`](#bool) | | | #### Field Rules @@ -1511,21 +2090,25 @@ An OAuth client on the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `name` | [`string`](#string) | | | -| `description` | [`string`](#string) | | | +| `ids` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | | The identifiers of the OAuth client. These are public and can be seen by any authenticated user in the network. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the OAuth client was created. This information is public and can be seen by any authenticated user in the network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the OAuth client was last updated. This information is public and can be seen by any authenticated user in the network. | +| `deleted_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the OAuth client was deleted. This information is public and can be seen by any authenticated user in the network. | +| `name` | [`string`](#string) | | The name of the OAuth client. This information is public and can be seen by any authenticated user in the network. | +| `description` | [`string`](#string) | | A description for the OAuth client. This information is public and can be seen by any authenticated user in the network. | | `attributes` | [`Client.AttributesEntry`](#ttn.lorawan.v3.Client.AttributesEntry) | repeated | Key-value attributes for this client. Typically used for organizing clients or for storing integration-specific data. | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application. This information is public and can be seen by any authenticated user in the network. This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `technical_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | | `secret` | [`string`](#string) | | The client secret is only visible to collaborators of the client. | -| `redirect_uris` | [`string`](#string) | repeated | The allowed redirect URIs against which authorization requests are checked. If the authorization request does not pass a redirect URI, the first one from this list is taken. | -| `logout_redirect_uris` | [`string`](#string) | repeated | The allowed logout redirect URIs against which client initiated logout requests are checked. If the authorization request does not pass a redirect URI, the first one from this list is taken. | -| `state` | [`State`](#ttn.lorawan.v3.State) | | The reviewing state of the client. This field can only be modified by admins. | -| `skip_authorization` | [`bool`](#bool) | | If set, the authorization page will be skipped. This field can only be modified by admins. | -| `endorsed` | [`bool`](#bool) | | If set, the authorization page will show endorsement. This field can only be modified by admins. | -| `grants` | [`GrantType`](#ttn.lorawan.v3.GrantType) | repeated | OAuth flows that can be used for the client to get a token. After a client is created, this field can only be modified by admins. | -| `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | Rights denotes what rights the client will have access to. Users that previously authorized this client will have to re-authorize the client after rights are added to this list. | +| `redirect_uris` | [`string`](#string) | repeated | The allowed redirect URIs against which authorization requests are checked. If the authorization request does not pass a redirect URI, the first one from this list is taken. This information is public and can be seen by any authenticated user in the network. | +| `logout_redirect_uris` | [`string`](#string) | repeated | The allowed logout redirect URIs against which client initiated logout requests are checked. If the authorization request does not pass a redirect URI, the first one from this list is taken. This information is public and can be seen by any authenticated user in the network. | +| `state` | [`State`](#ttn.lorawan.v3.State) | | The reviewing state of the client. This information is public and can be seen by any authenticated user in the network. This field can only be modified by admins. If state_description is not updated when updating state, state_description is cleared. | +| `state_description` | [`string`](#string) | | A description for the state field. This field can only be modified by admins, and should typically only be updated when also updating `state`. | +| `skip_authorization` | [`bool`](#bool) | | If set, the authorization page will be skipped. This information is public and can be seen by any authenticated user in the network. This field can only be modified by admins. | +| `endorsed` | [`bool`](#bool) | | If set, the authorization page will show endorsement. This information is public and can be seen by any authenticated user in the network. This field can only be modified by admins. | +| `grants` | [`GrantType`](#ttn.lorawan.v3.GrantType) | repeated | OAuth flows that can be used for the client to get a token. This information is public and can be seen by any authenticated user in the network. After a client is created, this field can only be modified by admins. | +| `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | Rights denotes what rights the client will have access to. This information is public and can be seen by any authenticated user in the network. Users that previously authorized this client will have to re-authorize the client after rights are added to this list. | #### Field Rules @@ -1534,8 +2117,13 @@ An OAuth client on the network. | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| +| `contact_info` |

`repeated.max_items`: `10`

| +| `secret` |

`string.max_len`: `128`

| +| `redirect_uris` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `128`

| +| `logout_redirect_uris` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `128`

| | `state` |

`enum.defined_only`: `true`

| +| `state_description` |

`string.max_len`: `128`

| | `grants` |

`repeated.items.enum.defined_only`: `true`

| | `rights` |

`repeated.items.enum.defined_only`: `true`

| @@ -1600,6 +2188,7 @@ An OAuth client on the network. | `client_ids` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | | | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | #### Field Rules @@ -1607,6 +2196,7 @@ An OAuth client on the network. | ----- | ----------- | | `client_ids` |

`message.required`: `true`

| | `limit` |

`uint32.lte`: `1000`

| +| `order` |

`string.in`: `[ id -id -rights rights]`

| ### Message `ListClientsRequest` @@ -1617,6 +2207,7 @@ An OAuth client on the network. | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted clients. | #### Field Rules @@ -1666,6 +2257,9 @@ The OAuth2 flows an OAuth client can use to get an access token. ### Service `ClientAccess` +The ClientAcces service, exposed by the Identity Server, is used to manage +collaborators of OAuth clients. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `ListRights` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this application. | @@ -1686,13 +2280,20 @@ The OAuth2 flows an OAuth client can use to get an access token. ### Service `ClientRegistry` +The ClientRegistry service, exposed by the Identity Server, is used to manage +OAuth client registrations. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `Create` | [`CreateClientRequest`](#ttn.lorawan.v3.CreateClientRequest) | [`Client`](#ttn.lorawan.v3.Client) | Create a new OAuth client. This also sets the given organization or user as first collaborator with all possible rights. | | `Get` | [`GetClientRequest`](#ttn.lorawan.v3.GetClientRequest) | [`Client`](#ttn.lorawan.v3.Client) | Get the OAuth client with the given identifiers, selecting the fields specified in the field mask. More or less fields may be returned, depending on the rights of the caller. | -| `List` | [`ListClientsRequest`](#ttn.lorawan.v3.ListClientsRequest) | [`Clients`](#ttn.lorawan.v3.Clients) | List OAuth clients where the given user or organization is a direct collaborator. If no user or organization is given, this returns the OAuth clients the caller has access to. Similar to Get, this selects the fields sepcified in the field mask. More or less fields may be returned, depending on the rights of the caller. | +| `List` | [`ListClientsRequest`](#ttn.lorawan.v3.ListClientsRequest) | [`Clients`](#ttn.lorawan.v3.Clients) | List OAuth clients where the given user or organization is a direct collaborator. If no user or organization is given, this returns the OAuth clients the caller has access to. Similar to Get, this selects the fields specified in the field mask. More or less fields may be returned, depending on the rights of the caller. | | `Update` | [`UpdateClientRequest`](#ttn.lorawan.v3.UpdateClientRequest) | [`Client`](#ttn.lorawan.v3.Client) | Update the OAuth client, changing the fields specified by the field mask to the provided values. | | `Delete` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the OAuth client. This may not release the client ID for reuse. | +| `Restore` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Restore a recently deleted client. + +Deployment configuration may specify if, and for how long after deletion, entities can be restored. | +| `Purge` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Purge the client. This will release the client ID for reuse. | #### HTTP bindings @@ -1706,6 +2307,8 @@ The OAuth2 flows an OAuth client can use to get an access token. | `List` | `GET` | `/api/v3/organizations/{collaborator.organization_ids.organization_id}/clients` | | | `Update` | `PUT` | `/api/v3/clients/{client.ids.client_id}` | `*` | | `Delete` | `DELETE` | `/api/v3/clients/{client_id}` | | +| `Restore` | `POST` | `/api/v3/clients/{client_id}/restore` | | +| `Purge` | `DELETE` | `/api/v3/clients/{client_id}/purge` | | ## File `lorawan-stack/api/cluster.proto` @@ -1729,176 +2332,1059 @@ PeerInfo ## File `lorawan-stack/api/configuration_services.proto` -### Message `FrequencyPlanDescription` +### Message `BandDescription` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `id` | [`string`](#string) | | | -| `base_id` | [`string`](#string) | | The ID of the frequency that the current frequency plan is based on. | -| `name` | [`string`](#string) | | | -| `base_frequency` | [`uint32`](#uint32) | | Base frequency in MHz for hardware support (433, 470, 868 or 915) | - -### Message `ListFrequencyPlansRequest` +| `beacon` | [`BandDescription.Beacon`](#ttn.lorawan.v3.BandDescription.Beacon) | | | +| `ping_slot_frequencies` | [`uint64`](#uint64) | repeated | | +| `max_uplink_channels` | [`uint32`](#uint32) | | | +| `uplink_channels` | [`BandDescription.Channel`](#ttn.lorawan.v3.BandDescription.Channel) | repeated | | +| `max_downlink_channels` | [`uint32`](#uint32) | | | +| `downlink_channels` | [`BandDescription.Channel`](#ttn.lorawan.v3.BandDescription.Channel) | repeated | | +| `sub_bands` | [`BandDescription.SubBandParameters`](#ttn.lorawan.v3.BandDescription.SubBandParameters) | repeated | | +| `data_rates` | [`BandDescription.DataRatesEntry`](#ttn.lorawan.v3.BandDescription.DataRatesEntry) | repeated | | +| `freq_multiplier` | [`uint64`](#uint64) | | | +| `implements_cf_list` | [`bool`](#bool) | | | +| `cf_list_type` | [`CFListType`](#ttn.lorawan.v3.CFListType) | | | +| `receive_delay_1` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `receive_delay_2` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `join_accept_delay_1` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `join_accept_delay_2` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `max_fcnt_gap` | [`uint64`](#uint64) | | | +| `supports_dynamic_adr` | [`bool`](#bool) | | | +| `adr_ack_limit` | [`ADRAckLimitExponent`](#ttn.lorawan.v3.ADRAckLimitExponent) | | | +| `min_retransmit_timeout` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `max_retransmit_timeout` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `tx_offset` | [`float`](#float) | repeated | | +| `max_adr_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | +| `tx_param_setup_req_support` | [`bool`](#bool) | | | +| `default_max_eirp` | [`float`](#float) | | | +| `default_rx2_parameters` | [`BandDescription.Rx2Parameters`](#ttn.lorawan.v3.BandDescription.Rx2Parameters) | | | +| `boot_dwell_time` | [`BandDescription.DwellTime`](#ttn.lorawan.v3.BandDescription.DwellTime) | | | + +### Message `BandDescription.BandDataRate` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `rate` | [`DataRate`](#ttn.lorawan.v3.DataRate) | | | + +### Message `BandDescription.Beacon` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | +| `coding_rate` | [`string`](#string) | | | +| `frequencies` | [`uint64`](#uint64) | repeated | | + +### Message `BandDescription.Channel` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `frequency` | [`uint64`](#uint64) | | | +| `min_data_rate` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | +| `max_data_rate` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | + +### Message `BandDescription.DataRatesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`uint32`](#uint32) | | | +| `value` | [`BandDescription.BandDataRate`](#ttn.lorawan.v3.BandDescription.BandDataRate) | | | + +### Message `BandDescription.DwellTime` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `uplinks` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | +| `downlinks` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | + +### Message `BandDescription.Rx2Parameters` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | +| `frequency` | [`uint64`](#uint64) | | | + +### Message `BandDescription.SubBandParameters` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `min_frequency` | [`uint64`](#uint64) | | | +| `max_frequency` | [`uint64`](#uint64) | | | +| `duty_cycle` | [`float`](#float) | | | +| `max_eirp` | [`float`](#float) | | | + +### Message `FrequencyPlanDescription` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [`string`](#string) | | | +| `base_id` | [`string`](#string) | | The ID of the frequency that the current frequency plan is based on. | +| `name` | [`string`](#string) | | | +| `base_frequency` | [`uint32`](#uint32) | | Base frequency in MHz for hardware support (433, 470, 868 or 915) | + +### Message `GetPhyVersionsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `band_id` | [`string`](#string) | | Optional Band ID to filter the results. If unused, all supported Bands and their versions are returned. | + +### Message `GetPhyVersionsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version_info` | [`GetPhyVersionsResponse.VersionInfo`](#ttn.lorawan.v3.GetPhyVersionsResponse.VersionInfo) | repeated | | + +### Message `GetPhyVersionsResponse.VersionInfo` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `band_id` | [`string`](#string) | | | +| `phy_versions` | [`PHYVersion`](#ttn.lorawan.v3.PHYVersion) | repeated | | + +### Message `ListBandsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `band_id` | [`string`](#string) | | Optional Band ID to filter the results. If unused, all supported Bands are returned. | +| `phy_version` | [`PHYVersion`](#ttn.lorawan.v3.PHYVersion) | | Optional PHY version to filter the results. If unused, all supported versions are returned. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `phy_version` |

`enum.defined_only`: `true`

| + +### Message `ListBandsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `descriptions` | [`ListBandsResponse.DescriptionsEntry`](#ttn.lorawan.v3.ListBandsResponse.DescriptionsEntry) | repeated | | + +### Message `ListBandsResponse.DescriptionsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`ListBandsResponse.VersionedBandDescription`](#ttn.lorawan.v3.ListBandsResponse.VersionedBandDescription) | | | + +### Message `ListBandsResponse.VersionedBandDescription` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `band` | [`ListBandsResponse.VersionedBandDescription.BandEntry`](#ttn.lorawan.v3.ListBandsResponse.VersionedBandDescription.BandEntry) | repeated | | + +### Message `ListBandsResponse.VersionedBandDescription.BandEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`BandDescription`](#ttn.lorawan.v3.BandDescription) | | | + +### Message `ListFrequencyPlansRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `base_frequency` | [`uint32`](#uint32) | | Optional base frequency in MHz for hardware support (433, 470, 868 or 915) | -### Message `ListFrequencyPlansResponse` +### Message `ListFrequencyPlansResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `frequency_plans` | [`FrequencyPlanDescription`](#ttn.lorawan.v3.FrequencyPlanDescription) | repeated | | + +### Service `Configuration` + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `ListFrequencyPlans` | [`ListFrequencyPlansRequest`](#ttn.lorawan.v3.ListFrequencyPlansRequest) | [`ListFrequencyPlansResponse`](#ttn.lorawan.v3.ListFrequencyPlansResponse) | | +| `GetPhyVersions` | [`GetPhyVersionsRequest`](#ttn.lorawan.v3.GetPhyVersionsRequest) | [`GetPhyVersionsResponse`](#ttn.lorawan.v3.GetPhyVersionsResponse) | Returns a list of supported LoRaWAN PHY Versions for the given Band ID. | +| `ListBands` | [`ListBandsRequest`](#ttn.lorawan.v3.ListBandsRequest) | [`ListBandsResponse`](#ttn.lorawan.v3.ListBandsResponse) | | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `ListFrequencyPlans` | `GET` | `/api/v3/configuration/frequency-plans` | | +| `GetPhyVersions` | `GET` | `/api/v3/configuration/phy-versions` | | +| `ListBands` | `GET` | `/api/v3/configuration/bands` | | +| `ListBands` | `GET` | `/api/v3/configuration/bands/{band_id}` | | +| `ListBands` | `GET` | `/api/v3/configuration/bands/{band_id}/{phy_version}` | | + +## File `lorawan-stack/api/contact_info.proto` + +### Message `ContactInfo` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `contact_type` | [`ContactType`](#ttn.lorawan.v3.ContactType) | | | +| `contact_method` | [`ContactMethod`](#ttn.lorawan.v3.ContactMethod) | | | +| `value` | [`string`](#string) | | | +| `public` | [`bool`](#bool) | | | +| `validated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `contact_type` |

`enum.defined_only`: `true`

| +| `contact_method` |

`enum.defined_only`: `true`

| +| `value` |

`string.max_len`: `256`

| + +### Message `ContactInfoValidation` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [`string`](#string) | | | +| `token` | [`string`](#string) | | | +| `entity` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | | | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `id` |

`string.min_len`: `1`

`string.max_len`: `64`

| +| `token` |

`string.min_len`: `1`

`string.max_len`: `64`

| + +### Enum `ContactMethod` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `CONTACT_METHOD_OTHER` | 0 | | +| `CONTACT_METHOD_EMAIL` | 1 | | +| `CONTACT_METHOD_PHONE` | 2 | | + +### Enum `ContactType` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `CONTACT_TYPE_OTHER` | 0 | | +| `CONTACT_TYPE_ABUSE` | 1 | | +| `CONTACT_TYPE_BILLING` | 2 | | +| `CONTACT_TYPE_TECHNICAL` | 3 | | + +### Service `ContactInfoRegistry` + +The ContactInfoRegistry service, exposed by the Identity Server, is used for +validating contact information of registered entities. + +The actual contact information can be managed with the different registry services: +ApplicationRegistry, ClientRegistry, GatewayRegistry, OrganizationRegistry and UserRegistry. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `RequestValidation` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | [`ContactInfoValidation`](#ttn.lorawan.v3.ContactInfoValidation) | Request validation for the non-validated contact info for the given entity. | +| `Validate` | [`ContactInfoValidation`](#ttn.lorawan.v3.ContactInfoValidation) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Validate confirms a contact info validation. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `RequestValidation` | `POST` | `/api/v3/contact_info/validation` | `*` | +| `Validate` | `PATCH` | `/api/v3/contact_info/validation` | `*` | + +## File `lorawan-stack/api/deviceclaimingserver.proto` + +### Message `AuthorizeApplicationRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `api_key` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| +| `api_key` |

`string.min_len`: `1`

`string.max_len`: `128`

| + +### Message `AuthorizeGatewayRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `api_key` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`message.required`: `true`

| +| `api_key` |

`string.min_len`: `1`

| + +### Message `CUPSRedirection` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `target_cups_uri` | [`string`](#string) | | CUPS URI for LoRa Basics Station CUPS redirection. | +| `current_gateway_key` | [`string`](#string) | | The key set in the gateway to authenticate itself. | +| `target_cups_trust` | [`bytes`](#bytes) | | Optional PEM encoded CA Root certificate. If this field is empty, DCS will attempt to dial the Target CUPS server and fetch the CA. | +| `client_tls` | [`CUPSRedirection.ClientTLS`](#ttn.lorawan.v3.CUPSRedirection.ClientTLS) | | TODO: Support mTLS (https://github.com/TheThingsNetwork/lorawan-stack/issues/137) | +| `auth_token` | [`string`](#string) | | The Device Claiming Server will fill this field with a The Things Stack API Key. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `target_cups_uri` |

`string.max_len`: `256`

`string.pattern`: `^https`

`string.uri`: `true`

| +| `current_gateway_key` |

`string.max_len`: `2048`

| +| `auth_token` |

`string.max_len`: `2048`

| + +### Message `CUPSRedirection.ClientTLS` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `cert` | [`bytes`](#bytes) | | PEM encoded Client Certificate. | +| `key` | [`bytes`](#bytes) | | PEM encoded Client Private Key. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `cert` |

`bytes.max_len`: `8192`

| +| `key` |

`bytes.max_len`: `8192`

| + +### Message `ClaimEndDeviceRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authenticated_identifiers` | [`ClaimEndDeviceRequest.AuthenticatedIdentifiers`](#ttn.lorawan.v3.ClaimEndDeviceRequest.AuthenticatedIdentifiers) | | Authenticated identifiers. | +| `qr_code` | [`bytes`](#bytes) | | Raw QR code contents. | +| `target_application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers of the target end device. | +| `target_device_id` | [`string`](#string) | | End device ID of the target end device. If empty, use the source device ID. | +| `target_network_server_address` | [`string`](#string) | | The address of the Network Server where the device will be registered. If set and if the source device is currently registered on a Network Server, settings will be transferred. If not set, the device shall not be registered on a Network Server. | +| `target_network_server_kek_label` | [`string`](#string) | | The KEK label of the Network Server to use for wrapping network session keys. | +| `target_application_server_address` | [`string`](#string) | | The address of the Application Server where the device will be registered. If set and if the source device is currently registered on an Application Server, settings will be transferred. If not set, the device shall not be registered on an Application Server. | +| `target_application_server_kek_label` | [`string`](#string) | | The KEK label of the Application Server to use for wrapping the application session key. | +| `target_application_server_id` | [`string`](#string) | | The AS-ID of the Application Server to use. | +| `target_net_id` | [`bytes`](#bytes) | | Home NetID. | +| `invalidate_authentication_code` | [`bool`](#bool) | | If set, invalidate the authentication code with which the device gets claimed. This prohibits subsequent claiming requests. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `qr_code` |

`bytes.min_len`: `0`

`bytes.max_len`: `1024`

| +| `target_application_ids` |

`message.required`: `true`

| +| `target_device_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `target_network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `target_network_server_kek_label` |

`string.max_len`: `2048`

| +| `target_application_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `target_application_server_kek_label` |

`string.max_len`: `2048`

| +| `target_application_server_id` |

`string.max_len`: `100`

| +| `target_net_id` |

`bytes.len`: `3`

| + +### Message `ClaimEndDeviceRequest.AuthenticatedIdentifiers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_eui` | [`bytes`](#bytes) | | JoinEUI (or AppEUI) of the device to claim. | +| `dev_eui` | [`bytes`](#bytes) | | DevEUI of the device to claim. | +| `authentication_code` | [`string`](#string) | | Authentication code to prove ownership. In the LoRa Alliance TR005 specification, this equals the OwnerToken. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| +| `dev_eui` |

`bytes.len`: `8`

| +| `authentication_code` |

`string.pattern`: `^[A-Z0-9]{1,32}$`

| + +### Message `ClaimGatewayRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `authenticated_identifiers` | [`ClaimGatewayRequest.AuthenticatedIdentifiers`](#ttn.lorawan.v3.ClaimGatewayRequest.AuthenticatedIdentifiers) | | | +| `qr_code` | [`bytes`](#bytes) | | | +| `collaborator` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | Collaborator to grant all rights on the target gateway. | +| `target_gateway_id` | [`string`](#string) | | Gateway ID for the target gateway. This must be a unique value. If this is not set, the target ID for the target gateway will be set to ``. | +| `target_gateway_server_address` | [`string`](#string) | | Target Gateway Server Address for the target gateway. | +| `cups_redirection` | [`CUPSRedirection`](#ttn.lorawan.v3.CUPSRedirection) | | Parameters to set CUPS redirection for the gateway. | +| `target_frequency_plan_id` | [`string`](#string) | | Frequency plan ID of the target gateway. This equals the first element of the frequency_plan_ids field. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `qr_code` |

`bytes.min_len`: `0`

`bytes.max_len`: `1024`

| +| `collaborator` |

`message.required`: `true`

| +| `target_gateway_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `target_gateway_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `target_frequency_plan_id` |

`string.max_len`: `64`

| + +### Message `ClaimGatewayRequest.AuthenticatedIdentifiers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_eui` | [`bytes`](#bytes) | | | +| `authentication_code` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_eui` |

`bytes.len`: `8`

| +| `authentication_code` |

`bytes.max_len`: `2048`

| + +### Message `GetClaimStatusResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `home_net_id` | [`bytes`](#bytes) | | | +| `home_ns_id` | [`bytes`](#bytes) | | | +| `vendor_specific` | [`GetClaimStatusResponse.VendorSpecific`](#ttn.lorawan.v3.GetClaimStatusResponse.VendorSpecific) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| +| `home_net_id` |

`bytes.len`: `3`

| +| `home_ns_id` |

`bytes.len`: `8`

| + +### Message `GetClaimStatusResponse.VendorSpecific` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `organization_unique_identifier` | [`uint32`](#uint32) | | | +| `data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Vendor Specific data in JSON format. | + +### Message `GetInfoByJoinEUIRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_eui` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + +### Message `GetInfoByJoinEUIResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_eui` | [`bytes`](#bytes) | | | +| `supports_claiming` | [`bool`](#bool) | | If set, this Join EUI is available for claiming on one of the configured Join Servers. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + +### Service `EndDeviceClaimingServer` + +The EndDeviceClaimingServer service configures authorization to claim end devices registered in an application, +and allows clients to claim end devices. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Claim` | [`ClaimEndDeviceRequest`](#ttn.lorawan.v3.ClaimEndDeviceRequest) | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | Claims the end device on a Join Server by claim authentication code or QR code. | +| `Unclaim` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Unclaims the end device on a Join Server. | +| `GetInfoByJoinEUI` | [`GetInfoByJoinEUIRequest`](#ttn.lorawan.v3.GetInfoByJoinEUIRequest) | [`GetInfoByJoinEUIResponse`](#ttn.lorawan.v3.GetInfoByJoinEUIResponse) | Return whether claiming is available for a given JoinEUI. | +| `GetClaimStatus` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`GetClaimStatusResponse`](#ttn.lorawan.v3.GetClaimStatusResponse) | Gets the claim status of an end device. | +| `AuthorizeApplication` | [`AuthorizeApplicationRequest`](#ttn.lorawan.v3.AuthorizeApplicationRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Authorize the End Device Claiming Server to claim devices registered in the given application. The application identifiers are the source application, where the devices are registered before they are claimed. The API key is used to access the application, find the device, verify the claim request and delete the end device from the source application. | +| `UnauthorizeApplication` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Unauthorize the End Device Claiming Server to claim devices in the given application. This reverts the authorization given with rpc AuthorizeApplication. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Claim` | `POST` | `/api/v3/edcs/claim` | `*` | +| `Unclaim` | `DELETE` | `/api/v3/edcs/claim/{application_ids.application_id}/devices/{device_id}` | `*` | +| `GetInfoByJoinEUI` | `POST` | `/api/v3/edcs/claim/info` | `*` | +| `GetClaimStatus` | `GET` | `/api/v3/edcs/claim/{application_ids.application_id}/devices/{device_id}` | | +| `AuthorizeApplication` | `POST` | `/api/v3/edcs/applications/{application_ids.application_id}/authorize` | `*` | +| `UnauthorizeApplication` | `DELETE` | `/api/v3/edcs/applications/{application_id}/authorize` | | + +### Service `GatewayClaimingServer` + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Claim` | [`ClaimGatewayRequest`](#ttn.lorawan.v3.ClaimGatewayRequest) | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user. | +| `AuthorizeGateway` | [`AuthorizeGatewayRequest`](#ttn.lorawan.v3.AuthorizeGatewayRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | AuthorizeGateway allows a gateway to be claimed. | +| `UnauthorizeGateway` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | UnauthorizeGateway prevents a gateway from being claimed. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Claim` | `POST` | `/api/v3/gcls/claim` | `*` | +| `AuthorizeGateway` | `POST` | `/api/v3/gcls/gateways/{gateway_ids.gateway_id}/authorize` | `*` | +| `UnauthorizeGateway` | `DELETE` | `/api/v3/gcls/gateways/{gateway_id}/authorize` | | + +## File `lorawan-stack/api/devicerepository.proto` + +### Message `DecodedMessagePayload` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `warnings` | [`string`](#string) | repeated | | +| `errors` | [`string`](#string) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `warnings` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `100`

| +| `errors` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `100`

| + +### Message `EncodedMessagePayload` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `f_port` | [`uint32`](#uint32) | | | +| `frm_payload` | [`bytes`](#bytes) | | | +| `warnings` | [`string`](#string) | repeated | | +| `errors` | [`string`](#string) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `f_port` |

`uint32.lte`: `255`

| +| `warnings` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `100`

| +| `errors` |

`repeated.max_items`: `10`

`repeated.items.string.max_len`: `100`

| + +### Message `EndDeviceBrand` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `brand_id` | [`string`](#string) | | Brand identifier, as specified in the Device Repository. | +| `name` | [`string`](#string) | | Brand name. | +| `private_enterprise_number` | [`uint32`](#uint32) | | Private Enterprise Number (PEN) assigned by IANA. | +| `organization_unique_identifiers` | [`string`](#string) | repeated | Organization Unique Identifiers (OUI) assigned by IEEE. | +| `lora_alliance_vendor_id` | [`uint32`](#uint32) | | VendorID managed by the LoRa Alliance, as defined in TR005. | +| `website` | [`string`](#string) | | Brand website URL. | +| `email` | [`string`](#string) | | Contact email address. | +| `logo` | [`string`](#string) | | Path to brand logo. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `organization_unique_identifiers` |

`repeated.items.string.pattern`: `[0-9A-F]{6}`

| +| `website` |

`string.uri`: `true`

| +| `email` |

`string.email`: `true`

| +| `logo` |

`string.pattern`: `^$|^(([a-z0-9-]+\/)+)?([a-z0-9_-]+\.)+(png|svg)$`

| + +### Message `EndDeviceModel` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `brand_id` | [`string`](#string) | | Brand identifier, as defined in the Device Repository. | +| `model_id` | [`string`](#string) | | Model identifier, as defined in the Device Repository. | +| `name` | [`string`](#string) | | Model name, as defined in the Device Repository. | +| `description` | [`string`](#string) | | Model description. | +| `hardware_versions` | [`EndDeviceModel.HardwareVersion`](#ttn.lorawan.v3.EndDeviceModel.HardwareVersion) | repeated | Available hardware versions. | +| `firmware_versions` | [`EndDeviceModel.FirmwareVersion`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion) | repeated | Available firmware versions. | +| `sensors` | [`string`](#string) | repeated | List of sensors included in the device. | +| `dimensions` | [`EndDeviceModel.Dimensions`](#ttn.lorawan.v3.EndDeviceModel.Dimensions) | | Device dimensions. | +| `weight` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Device weight (gram). | +| `battery` | [`EndDeviceModel.Battery`](#ttn.lorawan.v3.EndDeviceModel.Battery) | | Device battery information. | +| `operating_conditions` | [`EndDeviceModel.OperatingConditions`](#ttn.lorawan.v3.EndDeviceModel.OperatingConditions) | | Device operating conditions. | +| `ip_code` | [`string`](#string) | | Device IP rating code. | +| `key_provisioning` | [`KeyProvisioning`](#ttn.lorawan.v3.KeyProvisioning) | repeated | Supported key provisioning methods. | +| `key_security` | [`KeySecurity`](#ttn.lorawan.v3.KeySecurity) | | Device key security. | +| `photos` | [`EndDeviceModel.Photos`](#ttn.lorawan.v3.EndDeviceModel.Photos) | | Device photos. | +| `videos` | [`EndDeviceModel.Videos`](#ttn.lorawan.v3.EndDeviceModel.Videos) | | Device videos. | +| `product_url` | [`string`](#string) | | Device information page URL. | +| `datasheet_url` | [`string`](#string) | | Device datasheet URL. | +| `resellers` | [`EndDeviceModel.Reseller`](#ttn.lorawan.v3.EndDeviceModel.Reseller) | repeated | Reseller URLs. | +| `compliances` | [`EndDeviceModel.Compliances`](#ttn.lorawan.v3.EndDeviceModel.Compliances) | | List of standards the device is compliant with. | +| `additional_radios` | [`string`](#string) | repeated | List of any additional radios included in the device. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `model_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `sensors` |

`repeated.unique`: `true`

| +| `key_provisioning` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `key_security` |

`enum.defined_only`: `true`

| +| `product_url` |

`string.uri`: `true`

| +| `datasheet_url` |

`string.uri`: `true`

| +| `additional_radios` |

`repeated.unique`: `true`

| + +### Message `EndDeviceModel.Battery` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `replaceable` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether the device battery can be replaced. | +| `type` | [`string`](#string) | | Battery type. | + +### Message `EndDeviceModel.Compliances` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `safety` | [`EndDeviceModel.Compliances.Compliance`](#ttn.lorawan.v3.EndDeviceModel.Compliances.Compliance) | repeated | List of safety standards the device is compliant with. | +| `radio_equipment` | [`EndDeviceModel.Compliances.Compliance`](#ttn.lorawan.v3.EndDeviceModel.Compliances.Compliance) | repeated | List of radio equipment standards the device is compliant with. | + +### Message `EndDeviceModel.Compliances.Compliance` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `body` | [`string`](#string) | | | +| `norm` | [`string`](#string) | | | +| `standard` | [`string`](#string) | | | +| `version` | [`string`](#string) | | | + +### Message `EndDeviceModel.Dimensions` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `width` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Device width (mm). | +| `height` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Device height (mm). | +| `diameter` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Device diameter (mm). | +| `length` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Device length (mm). | + +### Message `EndDeviceModel.FirmwareVersion` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version` | [`string`](#string) | | Firmware version string. | +| `numeric` | [`uint32`](#uint32) | | Numeric firmware revision number. | +| `supported_hardware_versions` | [`string`](#string) | repeated | Hardware versions supported by this firmware version. | +| `profiles` | [`EndDeviceModel.FirmwareVersion.ProfilesEntry`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion.ProfilesEntry) | repeated | Device profiles for each supported region (band). | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `supported_hardware_versions` |

`repeated.unique`: `true`

| + +### Message `EndDeviceModel.FirmwareVersion.Profile` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `vendor_id` | [`string`](#string) | | Vendor ID of the profile, as defined in the Device Repository. If this value is set, the profile is loaded from this vendor's folder. If this value is not set, the profile is loaded from the current (end device's) vendor. | +| `profile_id` | [`string`](#string) | | Profile identifier, as defined in the Device Repository. | +| `lorawan_certified` | [`bool`](#bool) | | Whether the device is LoRaWAN certified. | +| `codec_id` | [`string`](#string) | | Payload formatter codec identifier, as defined in the Device Repository. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `vendor_id` |

`string.max_len`: `36`

`string.pattern`: `^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `profile_id` |

`string.max_len`: `36`

`string.pattern`: `^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `codec_id` |

`string.max_len`: `36`

`string.pattern`: `^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| + +### Message `EndDeviceModel.FirmwareVersion.ProfilesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`EndDeviceModel.FirmwareVersion.Profile`](#ttn.lorawan.v3.EndDeviceModel.FirmwareVersion.Profile) | | | + +### Message `EndDeviceModel.HardwareVersion` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `version` | [`string`](#string) | | Hardware version string. | +| `numeric` | [`uint32`](#uint32) | | Numberic hardware revision number. | +| `part_number` | [`string`](#string) | | Hardware part number. | + +### Message `EndDeviceModel.OperatingConditions` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `temperature` | [`EndDeviceModel.OperatingConditions.Limits`](#ttn.lorawan.v3.EndDeviceModel.OperatingConditions.Limits) | | Temperature operating conditions (Celsius). | +| `relative_humidity` | [`EndDeviceModel.OperatingConditions.Limits`](#ttn.lorawan.v3.EndDeviceModel.OperatingConditions.Limits) | | Relative humidity operating conditions (Fraction, in range [0, 1]). | + +### Message `EndDeviceModel.OperatingConditions.Limits` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `min` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Min value of operating conditions range. | +| `max` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Max value of operating conditions range. | + +### Message `EndDeviceModel.Photos` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `main` | [`string`](#string) | | Main device photo. | +| `other` | [`string`](#string) | repeated | List of other device photos. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `main` |

`string.pattern`: `^$|^(([a-z0-9-]+\/)+)?([a-z0-9_-]+\.)+(png|jpg|jpeg)$`

| +| `other` |

`repeated.unique`: `true`

`repeated.items.string.pattern`: `^$|^(([a-z0-9-]+\/)+)?([a-z0-9_-]+\.)+(png|jpg|jpeg)$`

| + +### Message `EndDeviceModel.Reseller` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `name` | [`string`](#string) | | Reseller name. | +| `region` | [`string`](#string) | repeated | Reseller regions. | +| `url` | [`string`](#string) | | Reseller URL. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `url` |

`string.uri`: `true`

| + +### Message `EndDeviceModel.Videos` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `main` | [`string`](#string) | | Link to main device video. | +| `other` | [`string`](#string) | repeated | Links to other device videos. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `main` |

`string.pattern`: `^(?:https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)(?:[\w\-_]*)(?:&(amp;)?[\w\?=]*)?)$|^(?:https?:\/\/(?:www\.)?vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(?:\d+)(?:|\/\?))$`

| +| `other` |

`repeated.unique`: `true`

`repeated.items.string.pattern`: `^(?:https?:\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)(?:[\w\-_]*)(?:&(amp;)?[\w\?=]*)?)$|^(?:https?:\/\/(?:www\.)?vimeo\.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(?:\d+)(?:|\/\?))$`

| + +### Message `GetEndDeviceBrandRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `frequency_plans` | [`FrequencyPlanDescription`](#ttn.lorawan.v3.FrequencyPlanDescription) | repeated | | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `brand_id` | [`string`](#string) | | Brand identifier, as defined in the Device Repository. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | Field mask paths. | -### Service `Configuration` +#### Field Rules -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `ListFrequencyPlans` | [`ListFrequencyPlansRequest`](#ttn.lorawan.v3.ListFrequencyPlansRequest) | [`ListFrequencyPlansResponse`](#ttn.lorawan.v3.ListFrequencyPlansResponse) | | +| Field | Validations | +| ----- | ----------- | +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| -#### HTTP bindings +### Message `GetEndDeviceModelRequest` -| Method Name | Method | Pattern | Body | -| ----------- | ------ | ------- | ---- | -| `ListFrequencyPlans` | `GET` | `/api/v3/configuration/frequency-plans` | | +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `brand_id` | [`string`](#string) | | Brand identifier, as defined in the Device Repository. | +| `model_id` | [`string`](#string) | | Model identifier, as defined in the Device Repository. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | Field mask paths. | -## File `lorawan-stack/api/contact_info.proto` +#### Field Rules -### Message `ContactInfo` +| Field | Validations | +| ----- | ----------- | +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `model_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| + +### Message `GetPayloadFormatterRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `contact_type` | [`ContactType`](#ttn.lorawan.v3.ContactType) | | | -| `contact_method` | [`ContactMethod`](#ttn.lorawan.v3.ContactMethod) | | | -| `value` | [`string`](#string) | | | -| `public` | [`bool`](#bool) | | | -| `validated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version information. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | Field mask paths. | -### Message `ContactInfoValidation` +### Message `GetTemplateRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `id` | [`string`](#string) | | | -| `token` | [`string`](#string) | | | -| `entity` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | | | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version information. Use either EndDeviceVersionIdentifiers or EndDeviceProfileIdentifiers. | +| `end_device_profile_ids` | [`GetTemplateRequest.EndDeviceProfileIdentifiers`](#ttn.lorawan.v3.GetTemplateRequest.EndDeviceProfileIdentifiers) | | End device profile identifiers. | -### Enum `ContactMethod` +### Message `GetTemplateRequest.EndDeviceProfileIdentifiers` -| Name | Number | Description | -| ---- | ------ | ----------- | -| `CONTACT_METHOD_OTHER` | 0 | | -| `CONTACT_METHOD_EMAIL` | 1 | | -| `CONTACT_METHOD_PHONE` | 2 | | +Identifiers to uniquely identify a LoRaWAN end device profile. -### Enum `ContactType` +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `vendor_id` | [`uint32`](#uint32) | | VendorID managed by the LoRa Alliance, as defined in TR005. | +| `vendor_profile_id` | [`uint32`](#uint32) | | ID of the LoRaWAN end device profile assigned by the vendor. | -| Name | Number | Description | -| ---- | ------ | ----------- | -| `CONTACT_TYPE_OTHER` | 0 | | -| `CONTACT_TYPE_ABUSE` | 1 | | -| `CONTACT_TYPE_BILLING` | 2 | | -| `CONTACT_TYPE_TECHNICAL` | 3 | | +#### Field Rules -### Service `ContactInfoRegistry` +| Field | Validations | +| ----- | ----------- | +| `vendor_id` |

`uint32.gte`: `1`

| -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `RequestValidation` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | [`ContactInfoValidation`](#ttn.lorawan.v3.ContactInfoValidation) | Request validation for the non-validated contact info for the given entity. | -| `Validate` | [`ContactInfoValidation`](#ttn.lorawan.v3.ContactInfoValidation) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Validate confirms a contact info validation. | +### Message `ListEndDeviceBrandsRequest` -#### HTTP bindings +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order_by` | [`string`](#string) | | Order (for pagination) | +| `search` | [`string`](#string) | | Search for brands matching a query string. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | Field mask paths. | -| Method Name | Method | Pattern | Body | -| ----------- | ------ | ------- | ---- | -| `RequestValidation` | `POST` | `/api/v3/contact_info/validation` | | -| `Validate` | `PATCH` | `/api/v3/contact_info/validation` | | +#### Field Rules -## File `lorawan-stack/api/deviceclaimingserver.proto` +| Field | Validations | +| ----- | ----------- | +| `limit` |

`uint32.lte`: `1000`

| +| `order_by` |

`string.in`: `[ brand_id -brand_id name -name]`

| +| `search` |

`string.max_len`: `100`

| -### Message `AuthorizeApplicationRequest` +### Message `ListEndDeviceBrandsResponse` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | -| `api_key` | [`string`](#string) | | | +| `brands` | [`EndDeviceBrand`](#ttn.lorawan.v3.EndDeviceBrand) | repeated | | + +### Message `ListEndDeviceModelsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers. | +| `brand_id` | [`string`](#string) | | List end devices from a specific brand. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order_by` | [`string`](#string) | | Order end devices | +| `search` | [`string`](#string) | | List end devices matching a query string. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | Field mask paths. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `application_ids` |

`message.required`: `true`

| -| `api_key` |

`string.min_len`: `1`

| +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$`

| +| `limit` |

`uint32.lte`: `1000`

| +| `order_by` |

`string.in`: `[ brand_id -brand_id model_id -model_id name -name]`

| +| `search` |

`string.max_len`: `100`

| -### Message `ClaimEndDeviceRequest` +### Message `ListEndDeviceModelsResponse` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `authenticated_identifiers` | [`ClaimEndDeviceRequest.AuthenticatedIdentifiers`](#ttn.lorawan.v3.ClaimEndDeviceRequest.AuthenticatedIdentifiers) | | | -| `qr_code` | [`bytes`](#bytes) | | | -| `target_application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | Application identifiers of the target end device. | -| `target_device_id` | [`string`](#string) | | End device ID of the target end device. If empty, use the source device ID. | -| `target_network_server_address` | [`string`](#string) | | The address of the Network Server where the device will be registered. If set and if the source device is currently registered on a Network Server, settings will be transferred. If not set, the device shall not be registered on a Network Server. | -| `target_network_server_kek_label` | [`string`](#string) | | The KEK label of the Network Server to use for wrapping network session keys. | -| `target_application_server_address` | [`string`](#string) | | The address of the Application Server where the device will be registered. If set and if the source device is currently registered on an Application Server, settings will be transferred. If not set, the device shall not be registered on an Application Server. | -| `target_application_server_kek_label` | [`string`](#string) | | The KEK label of the Application Server to use for wrapping the application session key. | -| `target_application_server_id` | [`string`](#string) | | The AS-ID of the Application Server to use. | -| `target_net_id` | [`bytes`](#bytes) | | Home NetID. | -| `invalidate_authentication_code` | [`bool`](#bool) | | If set, invalidate the authentication code with which the device gets claimed. This prohibits subsequent claiming requests. | +| `models` | [`EndDeviceModel`](#ttn.lorawan.v3.EndDeviceModel) | repeated | | + +### Message `MessagePayloadDecoder` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | Payload formatter type. | +| `formatter_parameter` | [`string`](#string) | | Parameter for the formatter, must be set together. | +| `codec_id` | [`string`](#string) | | | +| `examples` | [`MessagePayloadDecoder.Example`](#ttn.lorawan.v3.MessagePayloadDecoder.Example) | repeated | | #### Field Rules | Field | Validations | | ----- | ----------- | -| `qr_code` |

`bytes.min_len`: `0`

`bytes.max_len`: `1024`

| -| `target_application_ids` |

`message.required`: `true`

| -| `target_device_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| -| `target_network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| -| `target_network_server_kek_label` |

`string.max_len`: `2048`

| -| `target_application_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| -| `target_application_server_kek_label` |

`string.max_len`: `2048`

| -| `target_application_server_id` |

`string.max_len`: `100`

| +| `formatter` |

`enum.defined_only`: `true`

| +| `codec_id` |

`string.max_len`: `36`

`string.pattern`: `^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$`

| +| `examples` |

`repeated.max_items`: `20`

| -### Message `ClaimEndDeviceRequest.AuthenticatedIdentifiers` +### Message `MessagePayloadDecoder.Example` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `join_eui` | [`bytes`](#bytes) | | | -| `dev_eui` | [`bytes`](#bytes) | | | -| `authentication_code` | [`string`](#string) | | | +| `description` | [`string`](#string) | | | +| `input` | [`EncodedMessagePayload`](#ttn.lorawan.v3.EncodedMessagePayload) | | | +| `output` | [`DecodedMessagePayload`](#ttn.lorawan.v3.DecodedMessagePayload) | | | #### Field Rules | Field | Validations | | ----- | ----------- | -| `authentication_code` |

`string.pattern`: `^[A-Z0-9]{1,32}$`

| +| `description` |

`string.max_len`: `200`

| -### Service `EndDeviceClaimingServer` +### Message `MessagePayloadEncoder` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | Payload formatter type. | +| `formatter_parameter` | [`string`](#string) | | Parameter for the formatter, must be set together. | +| `codec_id` | [`string`](#string) | | | +| `examples` | [`MessagePayloadEncoder.Example`](#ttn.lorawan.v3.MessagePayloadEncoder.Example) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `formatter` |

`enum.defined_only`: `true`

| +| `codec_id` |

`string.max_len`: `36`

`string.pattern`: `^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$`

| +| `examples` |

`repeated.max_items`: `20`

| + +### Message `MessagePayloadEncoder.Example` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `description` | [`string`](#string) | | | +| `input` | [`DecodedMessagePayload`](#ttn.lorawan.v3.DecodedMessagePayload) | | | +| `output` | [`EncodedMessagePayload`](#ttn.lorawan.v3.EncodedMessagePayload) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `description` |

`string.max_len`: `200`

| + +### Enum `KeyProvisioning` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `KEY_PROVISIONING_UNKNOWN` | 0 | Unknown Key Provisioning. | +| `KEY_PROVISIONING_CUSTOM` | 1 | Custom Key Provisioning. | +| `KEY_PROVISIONING_JOIN_SERVER` | 2 | Key Provisioning from the Global Join Server. | +| `KEY_PROVISIONING_MANIFEST` | 3 | Key Provisioning from Manifest. | + +### Enum `KeySecurity` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `KEY_SECURITY_UNKNOWN` | 0 | Unknown key security. | +| `KEY_SECURITY_NONE` | 1 | No key security. | +| `KEY_SECURITY_READ_PROTECTED` | 2 | Read Protected key security. | +| `KEY_SECURITY_SECURE_ELEMENT` | 3 | Key security using the Security Element. | + +### Service `DeviceRepository` | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `Claim` | [`ClaimEndDeviceRequest`](#ttn.lorawan.v3.ClaimEndDeviceRequest) | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | Claims the end device by claim authentication code or QR code and transfers the device to the target application. | -| `AuthorizeApplication` | [`AuthorizeApplicationRequest`](#ttn.lorawan.v3.AuthorizeApplicationRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `UnauthorizeApplication` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| `ListBrands` | [`ListEndDeviceBrandsRequest`](#ttn.lorawan.v3.ListEndDeviceBrandsRequest) | [`ListEndDeviceBrandsResponse`](#ttn.lorawan.v3.ListEndDeviceBrandsResponse) | | +| `GetBrand` | [`GetEndDeviceBrandRequest`](#ttn.lorawan.v3.GetEndDeviceBrandRequest) | [`EndDeviceBrand`](#ttn.lorawan.v3.EndDeviceBrand) | | +| `ListModels` | [`ListEndDeviceModelsRequest`](#ttn.lorawan.v3.ListEndDeviceModelsRequest) | [`ListEndDeviceModelsResponse`](#ttn.lorawan.v3.ListEndDeviceModelsResponse) | | +| `GetModel` | [`GetEndDeviceModelRequest`](#ttn.lorawan.v3.GetEndDeviceModelRequest) | [`EndDeviceModel`](#ttn.lorawan.v3.EndDeviceModel) | | +| `GetTemplate` | [`GetTemplateRequest`](#ttn.lorawan.v3.GetTemplateRequest) | [`EndDeviceTemplate`](#ttn.lorawan.v3.EndDeviceTemplate) | | +| `GetUplinkDecoder` | [`GetPayloadFormatterRequest`](#ttn.lorawan.v3.GetPayloadFormatterRequest) | [`MessagePayloadDecoder`](#ttn.lorawan.v3.MessagePayloadDecoder) | | +| `GetDownlinkDecoder` | [`GetPayloadFormatterRequest`](#ttn.lorawan.v3.GetPayloadFormatterRequest) | [`MessagePayloadDecoder`](#ttn.lorawan.v3.MessagePayloadDecoder) | | +| `GetDownlinkEncoder` | [`GetPayloadFormatterRequest`](#ttn.lorawan.v3.GetPayloadFormatterRequest) | [`MessagePayloadEncoder`](#ttn.lorawan.v3.MessagePayloadEncoder) | | #### HTTP bindings | Method Name | Method | Pattern | Body | | ----------- | ------ | ------- | ---- | -| `Claim` | `POST` | `/api/v3/edcs/claim` | `*` | -| `AuthorizeApplication` | `POST` | `/api/v3/edcs/applications/{application_ids.application_id}/authorize` | `*` | -| `UnauthorizeApplication` | `DELETE` | `/api/v3/edcs/applications/{application_id}/authorize` | | +| `ListBrands` | `GET` | `/api/v3/dr/brands` | | +| `ListBrands` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands` | | +| `GetBrand` | `GET` | `/api/v3/dr/brands/{brand_id}` | | +| `GetBrand` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{brand_id}` | | +| `ListModels` | `GET` | `/api/v3/dr/models` | | +| `ListModels` | `GET` | `/api/v3/dr/brands/{brand_id}/models` | | +| `ListModels` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/models` | | +| `ListModels` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{brand_id}/models` | | +| `GetModel` | `GET` | `/api/v3/dr/brands/{brand_id}/models/{model_id}` | | +| `GetModel` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{brand_id}/models/{model_id}` | | +| `GetTemplate` | `GET` | `/api/v3/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template` | | +| `GetTemplate` | `GET` | `/api/v3/dr/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template` | | +| `GetTemplate` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template` | | +| `GetTemplate` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template` | | +| `GetUplinkDecoder` | `GET` | `/api/v3/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder` | | +| `GetUplinkDecoder` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder` | | +| `GetDownlinkDecoder` | `GET` | `/api/v3/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder` | | +| `GetDownlinkDecoder` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder` | | +| `GetDownlinkEncoder` | `GET` | `/api/v3/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder` | | +| `GetDownlinkEncoder` | `GET` | `/api/v3/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder` | | + +## File `lorawan-stack/api/email_messages.proto` + +### Message `CreateClientEmailMessage` + +CreateClientEmailMessage is used as a wrapper for handling the email regarding the create client procedure. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `create_client_request` | [`CreateClientRequest`](#ttn.lorawan.v3.CreateClientRequest) | | | +| `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | ## File `lorawan-stack/api/end_device.proto` +### Message `ADRSettings` + +Adaptive Data Rate settings. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `static` | [`ADRSettings.StaticMode`](#ttn.lorawan.v3.ADRSettings.StaticMode) | | | +| `dynamic` | [`ADRSettings.DynamicMode`](#ttn.lorawan.v3.ADRSettings.DynamicMode) | | | +| `disabled` | [`ADRSettings.DisabledMode`](#ttn.lorawan.v3.ADRSettings.DisabledMode) | | | + +### Message `ADRSettings.DisabledMode` + +Configuration options for cases in which ADR is to be disabled +completely. + +### Message `ADRSettings.DynamicMode` + +Configuration options for dynamic ADR. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `margin` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | The ADR margin (dB) tells the network server how much margin it should add in ADR requests. A bigger margin is less efficient, but gives a better chance of successful reception. If unset, the default value from Network Server configuration will be used. | +| `min_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | Minimum data rate index. If unset, the default value from Network Server configuration will be used. | +| `max_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | Maximum data rate index. If unset, the default value from Network Server configuration will be used. | +| `min_tx_power_index` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Minimum transmission power index. If unset, the default value from Network Server configuration will be used. | +| `max_tx_power_index` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Maximum transmission power index. If unset, the default value from Network Server configuration will be used. | +| `min_nb_trans` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Minimum number of retransmissions. If unset, the default value from Network Server configuration will be used. | +| `max_nb_trans` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Maximum number of retransmissions. If unset, the default value from Network Server configuration will be used. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `min_tx_power_index` |

`uint32.lte`: `15`

| +| `max_tx_power_index` |

`uint32.lte`: `15`

| +| `min_nb_trans` |

`uint32.lte`: `3`

`uint32.gte`: `1`

| +| `max_nb_trans` |

`uint32.lte`: `3`

`uint32.gte`: `1`

| + +### Message `ADRSettings.StaticMode` + +Configuration options for static ADR. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | Data rate index to use. | +| `tx_power_index` | [`uint32`](#uint32) | | Transmission power index to use. | +| `nb_trans` | [`uint32`](#uint32) | | Number of retransmissions. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `data_rate_index` |

`enum.defined_only`: `true`

| +| `tx_power_index` |

`uint32.lte`: `15`

| +| `nb_trans` |

`uint32.lte`: `15`

`uint32.gte`: `1`

| + +### Message `BatchUpdateEndDeviceLastSeenRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `updates` | [`BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate`](#ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate) | repeated | The last seen timestamp needs to be set per end device. | + +### Message `BatchUpdateEndDeviceLastSeenRequest.EndDeviceLastSeenUpdate` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `last_seen_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `ids` |

`message.required`: `true`

| + +### Message `BoolValue` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `value` | [`bool`](#bool) | | | + ### Message `ConvertEndDeviceTemplateRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `format_id` | [`string`](#string) | | ID of the format. | | `data` | [`bytes`](#bytes) | | Data to convert. | +| `end_device_version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device profile identifiers. | #### Field Rules @@ -1918,6 +3404,19 @@ PeerInfo | ----- | ----------- | | `end_device` |

`message.required`: `true`

| +### Message `DevAddrPrefix` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dev_addr` | [`bytes`](#bytes) | | DevAddr base. | +| `length` | [`uint32`](#uint32) | | Number of most significant bits from dev_addr that are used as prefix. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `dev_addr` |

`bytes.len`: `4`

| + ### Message `EndDevice` Defines an End Device registration and its state on the network. @@ -1967,17 +3466,16 @@ SDKs are responsible for combining (if desired) the three. | `power_state` | [`PowerState`](#ttn.lorawan.v3.PowerState) | | The power state of the device; whether it is battery-powered or connected to an external power source. Received via the DevStatus MAC command at status_received_at. Stored in Network Server. | | `battery_percentage` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Latest-known battery percentage of the device. Received via the DevStatus MAC command at last_dev_status_received_at or earlier. Stored in Network Server. | | `downlink_margin` | [`int32`](#int32) | | Demodulation signal-to-noise ratio (dB). Received via the DevStatus MAC command at last_dev_status_received_at. Stored in Network Server. | -| `recent_adr_uplinks` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | repeated | Recent uplink messages with ADR bit set to 1 sorted by time. Stored in Network Server. The field is reset each time an uplink message carrying MACPayload is received with ADR bit set to 0. The number of messages stored is in the range [0,20]; | -| `recent_uplinks` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | repeated | Recent uplink messages sorted by time. Stored in Network Server. The number of messages stored may depend on configuration. | -| `recent_downlinks` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | repeated | Recent downlink messages sorted by time. Stored in Network Server. The number of messages stored may depend on configuration. | | `queued_application_downlinks` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | repeated | Queued Application downlink messages. Stored in Application Server, which sets them on the Network Server. This field is deprecated and is always set equal to session.queued_application_downlinks. | | `formatters` | [`MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) | | The payload formatters for this end device. Stored in Application Server. Copied on creation from template identified by version_ids. | | `provisioner_id` | [`string`](#string) | | ID of the provisioner. Stored in Join Server. | | `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Vendor-specific provisioning data. Stored in Join Server. | | `multicast` | [`bool`](#bool) | | Indicates whether this device represents a multicast group. | -| `claim_authentication_code` | [`EndDeviceAuthenticationCode`](#ttn.lorawan.v3.EndDeviceAuthenticationCode) | | Authentication code to claim ownership of the end device. Stored in Join Server. | +| `claim_authentication_code` | [`EndDeviceAuthenticationCode`](#ttn.lorawan.v3.EndDeviceAuthenticationCode) | | Authentication code to claim ownership of the end device. From TTS v3.21.0 this field is stored in the Identity Server. For TTS versions < 3.21.0, this field is stored in the Join Server. The value stored on the Identity Server takes precedence. | | `skip_payload_crypto` | [`bool`](#bool) | | Skip decryption of uplink payloads and encryption of downlink payloads. This field is deprecated, use skip_payload_crypto_override instead. | | `skip_payload_crypto_override` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Skip decryption of uplink payloads and encryption of downlink payloads. This field overrides the application-level setting. | +| `activated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp when the device has been activated. Stored in the Entity Registry. This field is set by the Application Server when an end device sends its first uplink. The Application Server will use the field in order to avoid repeated calls to the Entity Registry. The field cannot be unset once set. | +| `last_seen_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp when a device uplink has been last observed. This field is set by the Application Server and stored in the Identity Server. | #### Field Rules @@ -1986,7 +3484,7 @@ SDKs are responsible for combining (if desired) the three. | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| | `service_profile_id` |

`string.max_len`: `64`

| | `network_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| | `network_server_kek_label` |

`string.max_len`: `2048`

| @@ -1998,6 +3496,7 @@ SDKs are responsible for combining (if desired) the three. | `lorawan_version` |

`enum.defined_only`: `true`

| | `lorawan_phy_version` |

`enum.defined_only`: `true`

| | `frequency_plan_id` |

`string.max_len`: `64`

| +| `net_id` |

`bytes.len`: `3`

| | `power_state` |

`enum.defined_only`: `true`

| | `battery_percentage` |

`float.lte`: `1`

`float.gte`: `0`

| | `provisioner_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| @@ -2030,31 +3529,7 @@ Authentication code for end devices. | Field | Validations | | ----- | ----------- | -| `value` |

`string.pattern`: `^[A-Z0-9]{1,32}$`

| - -### Message `EndDeviceBrand` - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `id` | [`string`](#string) | | | -| `name` | [`string`](#string) | | | -| `url` | [`string`](#string) | | | -| `logos` | [`string`](#string) | repeated | Logos contains file names of brand logos. | - -### Message `EndDeviceModel` - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `brand_id` | [`string`](#string) | | | -| `id` | [`string`](#string) | | | -| `name` | [`string`](#string) | | | - -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| -| `id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `value` |

`string.pattern`: `^[a-zA-Z0-9]{1,32}$`

| ### Message `EndDeviceTemplate` @@ -2116,43 +3591,26 @@ Template for creating end devices. | `lorawan_version` | [`MACVersion`](#ttn.lorawan.v3.MACVersion) | | LoRaWAN MAC version. | | `lorawan_phy_version` | [`PHYVersion`](#ttn.lorawan.v3.PHYVersion) | | LoRaWAN PHY version. | | `frequency_plan_id` | [`string`](#string) | | ID of the frequency plan used by this device. | -| `photos` | [`string`](#string) | repeated | Photos contains file names of device photos. | -| `supports_class_b` | [`bool`](#bool) | | Whether the device supports class B. | -| `supports_class_c` | [`bool`](#bool) | | Whether the device supports class C. | -| `default_mac_settings` | [`MACSettings`](#ttn.lorawan.v3.MACSettings) | | Default MAC layer settings of the device. | -| `min_frequency` | [`uint64`](#uint64) | | Minimum frequency the device is capable of using (Hz). | -| `max_frequency` | [`uint64`](#uint64) | | Maximum frequency the device is capable of using (Hz). | -| `supports_join` | [`bool`](#bool) | | The device supports join (it's OTAA). | -| `resets_join_nonces` | [`bool`](#bool) | | Whether the device resets the join and dev nonces (not LoRaWAN compliant). | -| `default_formatters` | [`MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) | | Default formatters defining the payload formats for this end device. | - -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `ids` |

`message.required`: `true`

| -| `lorawan_version` |

`enum.defined_only`: `true`

| -| `lorawan_phy_version` |

`enum.defined_only`: `true`

| -| `frequency_plan_id` |

`string.max_len`: `64`

| -| `default_formatters` |

`message.required`: `true`

| - -### Message `EndDeviceVersionIdentifiers` - -Identifies an end device model with version information. - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `brand_id` | [`string`](#string) | | | -| `model_id` | [`string`](#string) | | | -| `hardware_version` | [`string`](#string) | | | -| `firmware_version` | [`string`](#string) | | | +| `photos` | [`string`](#string) | repeated | Photos contains file names of device photos. | +| `supports_class_b` | [`bool`](#bool) | | Whether the device supports class B. | +| `supports_class_c` | [`bool`](#bool) | | Whether the device supports class C. | +| `default_mac_settings` | [`MACSettings`](#ttn.lorawan.v3.MACSettings) | | Default MAC layer settings of the device. | +| `min_frequency` | [`uint64`](#uint64) | | Minimum frequency the device is capable of using (Hz). | +| `max_frequency` | [`uint64`](#uint64) | | Maximum frequency the device is capable of using (Hz). | +| `supports_join` | [`bool`](#bool) | | The device supports join (it's OTAA). | +| `resets_join_nonces` | [`bool`](#bool) | | Whether the device resets the join and dev nonces (not LoRaWAN compliant). | +| `default_formatters` | [`MessagePayloadFormatters`](#ttn.lorawan.v3.MessagePayloadFormatters) | | Default formatters defining the payload formats for this end device. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| -| `model_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `ids` |

`message.required`: `true`

| +| `lorawan_version` |

`enum.defined_only`: `true`

| +| `lorawan_phy_version` |

`enum.defined_only`: `true`

| +| `frequency_plan_id` |

`string.max_len`: `64`

| +| `photos` |

`repeated.max_items`: `10`

| +| `default_formatters` |

`message.required`: `true`

| ### Message `EndDevices` @@ -2167,6 +3625,13 @@ Identifies an end device model with version information. | `join_eui` | [`bytes`](#bytes) | | | | `dev_eui` | [`bytes`](#bytes) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| +| `dev_eui` |

`bytes.len`: `8`

| + ### Message `GetEndDeviceRequest` | Field | Type | Label | Description | @@ -2194,7 +3659,6 @@ Identifies an end device model with version information. | Field | Validations | | ----- | ----------- | -| `application_ids` |

`message.required`: `true`

| | `order` |

`string.in`: `[ device_id -device_id join_eui -join_eui dev_eui -dev_eui name -name description -description created_at -created_at]`

| | `limit` |

`uint32.lte`: `1000`

| @@ -2212,7 +3676,7 @@ This is used internally by the Network Server. | `adr_ack_limit` | [`uint32`](#uint32) | | ADR: number of messages to wait before setting ADRAckReq. This field is deprecated, use adr_ack_limit_exponent instead. | | `adr_ack_delay` | [`uint32`](#uint32) | | ADR: number of messages to wait after setting ADRAckReq and before changing TxPower or DataRate. This field is deprecated, use adr_ack_delay_exponent instead. | | `rx1_delay` | [`RxDelay`](#ttn.lorawan.v3.RxDelay) | | Rx1 delay (Rx2 delay is Rx1 delay + 1 second). | -| `rx1_data_rate_offset` | [`uint32`](#uint32) | | Data rate offset for Rx1. | +| `rx1_data_rate_offset` | [`DataRateOffset`](#ttn.lorawan.v3.DataRateOffset) | | Data rate offset for Rx1. | | `rx2_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | Data rate index for Rx2. | | `rx2_frequency` | [`uint64`](#uint64) | | Frequency for Rx2 (Hz). | | `max_duty_cycle` | [`AggregatedDutyCycle`](#ttn.lorawan.v3.AggregatedDutyCycle) | | Maximum uplink duty cycle (of all channels). | @@ -2222,8 +3686,8 @@ This is used internally by the Network Server. | `ping_slot_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | Data rate index of the class B ping slot. This field is deprecated, use ping_slot_data_rate_index_value instead. | | `beacon_frequency` | [`uint64`](#uint64) | | Frequency of the class B beacon (Hz). | | `channels` | [`MACParameters.Channel`](#ttn.lorawan.v3.MACParameters.Channel) | repeated | Configured uplink channels and optionally Rx1 frequency. | -| `uplink_dwell_time` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether uplink dwell time is set (400ms). If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). | -| `downlink_dwell_time` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether downlink dwell time is set (400ms). If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). | +| `uplink_dwell_time` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether uplink dwell time is set (400ms). If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). | +| `downlink_dwell_time` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether downlink dwell time is set (400ms). If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). | | `adr_ack_limit_exponent` | [`ADRAckLimitExponentValue`](#ttn.lorawan.v3.ADRAckLimitExponentValue) | | ADR: number of messages to wait before setting ADRAckReq. | | `adr_ack_delay_exponent` | [`ADRAckDelayExponentValue`](#ttn.lorawan.v3.ADRAckDelayExponentValue) | | ADR: number of messages to wait after setting ADRAckReq and before changing TxPower or DataRate. | | `ping_slot_data_rate_index_value` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | Data rate index of the class B ping slot. | @@ -2236,7 +3700,7 @@ This is used internally by the Network Server. | `adr_tx_power_index` |

`uint32.lte`: `15`

| | `adr_nb_trans` |

`uint32.lte`: `15`

| | `rx1_delay` |

`enum.defined_only`: `true`

| -| `rx1_data_rate_offset` |

`uint32.lte`: `7`

| +| `rx1_data_rate_offset` |

`enum.defined_only`: `true`

| | `rx2_data_rate_index` |

`enum.defined_only`: `true`

| | `rx2_frequency` |

`uint64.gte`: `100000`

| | `max_duty_cycle` |

`enum.defined_only`: `true`

| @@ -2260,7 +3724,7 @@ This is used internally by the Network Server. | Field | Validations | | ----- | ----------- | -| `uplink_frequency` |

`uint64.gte`: `100000`

| +| `uplink_frequency` |

`uint64.lte`: `0`

`uint64.gte`: `100000`

| | `downlink_frequency` |

`uint64.gte`: `100000`

| | `min_data_rate_index` |

`enum.defined_only`: `true`

| | `max_data_rate_index` |

`enum.defined_only`: `true`

| @@ -2272,49 +3736,49 @@ This is used internally by the Network Server. | `class_b_timeout` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Maximum delay for the device to answer a MAC request or a confirmed downlink frame. If unset, the default value from Network Server configuration will be used. | | `ping_slot_periodicity` | [`PingSlotPeriodValue`](#ttn.lorawan.v3.PingSlotPeriodValue) | | Periodicity of the class B ping slot. If unset, the default value from Network Server configuration will be used. | | `ping_slot_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | Data rate index of the class B ping slot. If unset, the default value from Network Server configuration will be used. | -| `ping_slot_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | Frequency of the class B ping slot (Hz). If unset, the default value from Network Server configuration will be used. | -| `beacon_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | Frequency of the class B beacon (Hz). If unset, the default value from Network Server configuration will be used. | +| `ping_slot_frequency` | [`ZeroableFrequencyValue`](#ttn.lorawan.v3.ZeroableFrequencyValue) | | Frequency of the class B ping slot (Hz). If unset, the default value from Network Server configuration will be used. | +| `beacon_frequency` | [`ZeroableFrequencyValue`](#ttn.lorawan.v3.ZeroableFrequencyValue) | | Frequency of the class B beacon (Hz). If unset, the default value from Network Server configuration will be used. | | `class_c_timeout` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Maximum delay for the device to answer a MAC request or a confirmed downlink frame. If unset, the default value from Network Server configuration will be used. | | `rx1_delay` | [`RxDelayValue`](#ttn.lorawan.v3.RxDelayValue) | | Class A Rx1 delay. If unset, the default value from Network Server configuration or regional parameters specification will be used. | -| `rx1_data_rate_offset` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Rx1 data rate offset. If unset, the default value from Network Server configuration will be used. | +| `rx1_data_rate_offset` | [`DataRateOffsetValue`](#ttn.lorawan.v3.DataRateOffsetValue) | | Rx1 data rate offset. If unset, the default value from Network Server configuration will be used. | | `rx2_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | Data rate index for Rx2. If unset, the default value from Network Server configuration or regional parameters specification will be used. | -| `rx2_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | Frequency for Rx2 (Hz). If unset, the default value from Network Server configuration or regional parameters specification will be used. | +| `rx2_frequency` | [`FrequencyValue`](#ttn.lorawan.v3.FrequencyValue) | | Frequency for Rx2 (Hz). If unset, the default value from Network Server configuration or regional parameters specification will be used. | | `factory_preset_frequencies` | [`uint64`](#uint64) | repeated | List of factory-preset frequencies. If unset, the default value from Network Server configuration or regional parameters specification will be used. | | `max_duty_cycle` | [`AggregatedDutyCycleValue`](#ttn.lorawan.v3.AggregatedDutyCycleValue) | | Maximum uplink duty cycle (of all channels). | -| `supports_32_bit_f_cnt` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether the device supports 32-bit frame counters. If unset, the default value from Network Server configuration will be used. | -| `use_adr` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether the Network Server should use ADR for the device. If unset, the default value from Network Server configuration will be used. | -| `adr_margin` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | The ADR margin tells the network server how much margin it should add in ADR requests. A bigger margin is less efficient, but gives a better chance of successful reception. If unset, the default value from Network Server configuration will be used. | -| `resets_f_cnt` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether the device resets the frame counters (not LoRaWAN compliant). If unset, the default value from Network Server configuration will be used. | +| `supports_32_bit_f_cnt` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether the device supports 32-bit frame counters. If unset, the default value from Network Server configuration will be used. | +| `use_adr` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether the Network Server should use ADR for the device. This field is deprecated, use adr_settings instead. | +| `adr_margin` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | The ADR margin (dB) tells the network server how much margin it should add in ADR requests. A bigger margin is less efficient, but gives a better chance of successful reception. This field is deprecated, use adr_settings.dynamic.margin instead. | +| `resets_f_cnt` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether the device resets the frame counters (not LoRaWAN compliant). If unset, the default value from Network Server configuration will be used. | | `status_time_periodicity` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | The interval after which a DevStatusReq MACCommand shall be sent. If unset, the default value from Network Server configuration will be used. | | `status_count_periodicity` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | Number of uplink messages after which a DevStatusReq MACCommand shall be sent. If unset, the default value from Network Server configuration will be used. | | `desired_rx1_delay` | [`RxDelayValue`](#ttn.lorawan.v3.RxDelayValue) | | The Rx1 delay Network Server should configure device to use via MAC commands or Join-Accept. If unset, the default value from Network Server configuration or regional parameters specification will be used. | -| `desired_rx1_data_rate_offset` | [`google.protobuf.UInt32Value`](#google.protobuf.UInt32Value) | | The Rx1 data rate offset Network Server should configure device to use via MAC commands or Join-Accept. If unset, the default value from Network Server configuration will be used. | +| `desired_rx1_data_rate_offset` | [`DataRateOffsetValue`](#ttn.lorawan.v3.DataRateOffsetValue) | | The Rx1 data rate offset Network Server should configure device to use via MAC commands or Join-Accept. If unset, the default value from Network Server configuration will be used. | | `desired_rx2_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | The Rx2 data rate index Network Server should configure device to use via MAC commands or Join-Accept. If unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used. | -| `desired_rx2_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | The Rx2 frequency index Network Server should configure device to use via MAC commands. If unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used. | +| `desired_rx2_frequency` | [`FrequencyValue`](#ttn.lorawan.v3.FrequencyValue) | | The Rx2 frequency index Network Server should configure device to use via MAC commands. If unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used. | | `desired_max_duty_cycle` | [`AggregatedDutyCycleValue`](#ttn.lorawan.v3.AggregatedDutyCycleValue) | | The maximum uplink duty cycle (of all channels) Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration will be used. | | `desired_adr_ack_limit_exponent` | [`ADRAckLimitExponentValue`](#ttn.lorawan.v3.ADRAckLimitExponentValue) | | The ADR ACK limit Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration or regional parameters specification will be used. | | `desired_adr_ack_delay_exponent` | [`ADRAckDelayExponentValue`](#ttn.lorawan.v3.ADRAckDelayExponentValue) | | The ADR ACK delay Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration or regional parameters specification will be used. | | `desired_ping_slot_data_rate_index` | [`DataRateIndexValue`](#ttn.lorawan.v3.DataRateIndexValue) | | The data rate index of the class B ping slot Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration will be used. | -| `desired_ping_slot_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | The frequency of the class B ping slot (Hz) Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration or regional parameters specification will be used. | -| `desired_beacon_frequency` | [`google.protobuf.UInt64Value`](#google.protobuf.UInt64Value) | | The frequency of the class B beacon (Hz) Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration will be used. | +| `desired_ping_slot_frequency` | [`ZeroableFrequencyValue`](#ttn.lorawan.v3.ZeroableFrequencyValue) | | The frequency of the class B ping slot (Hz) Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration or regional parameters specification will be used. | +| `desired_beacon_frequency` | [`ZeroableFrequencyValue`](#ttn.lorawan.v3.ZeroableFrequencyValue) | | The frequency of the class B beacon (Hz) Network Server should configure device to use via MAC commands. If unset, the default value from Network Server configuration will be used. | +| `desired_max_eirp` | [`DeviceEIRPValue`](#ttn.lorawan.v3.DeviceEIRPValue) | | Maximum EIRP (dBm). If unset, the default value from regional parameters specification will be used. | +| `class_b_c_downlink_interval` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | The minimum duration passed before a network-initiated(e.g. Class B or C) downlink following an arbitrary downlink. | +| `uplink_dwell_time` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether uplink dwell time is set (400ms). If unset, the default value from Network Server configuration or regional parameters specification will be used. | +| `downlink_dwell_time` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether downlink dwell time is set (400ms). If unset, the default value from Network Server configuration or regional parameters specification will be used. | +| `adr` | [`ADRSettings`](#ttn.lorawan.v3.ADRSettings) | | Adaptive Data Rate settings. If unset, the default value from Network Server configuration or regional parameters specification will be used. | +| `schedule_downlinks` | [`BoolValue`](#ttn.lorawan.v3.BoolValue) | | Whether or not downlink messages should be scheduled. This option can be used in order to disable any downlink interaction with the end device. It will affect all types of downlink messages: data and MAC downlinks, and join accepts. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `ping_slot_frequency` |

`uint64.gte`: `100000`

| -| `beacon_frequency` |

`uint64.gte`: `100000`

| -| `rx1_data_rate_offset` |

`uint32.lte`: `7`

| -| `rx2_frequency` |

`uint64.gte`: `100000`

| -| `desired_rx2_frequency` |

`uint64.gte`: `100000`

| -| `desired_ping_slot_frequency` |

`uint64.gte`: `100000`

| -| `desired_beacon_frequency` |

`uint64.gte`: `100000`

| +| `factory_preset_frequencies` |

`repeated.max_items`: `96`

| ### Message `MACState` MACState represents the state of MAC layer of the device. MACState is reset on each join for OTAA or ResetInd for ABP devices. -This is used internally by the Network Server and is read only. +This is used internally by the Network Server. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | @@ -2329,15 +3793,18 @@ This is used internally by the Network Server and is read only. | `queued_responses` | [`MACCommand`](#ttn.lorawan.v3.MACCommand) | repeated | Queued MAC responses. Regenerated on each uplink. | | `pending_requests` | [`MACCommand`](#ttn.lorawan.v3.MACCommand) | repeated | Pending MAC requests(i.e. sent requests, for which no response has been received yet). Regenerated on each downlink. | | `queued_join_accept` | [`MACState.JoinAccept`](#ttn.lorawan.v3.MACState.JoinAccept) | | Queued join-accept. Set each time a (re-)join request accept is received from Join Server and removed each time a downlink is scheduled. | -| `pending_join_request` | [`JoinRequest`](#ttn.lorawan.v3.JoinRequest) | | Pending join request. Set each time a join accept is scheduled and removed each time an uplink is received from the device. | +| `pending_join_request` | [`MACState.JoinRequest`](#ttn.lorawan.v3.MACState.JoinRequest) | | Pending join request. Set each time a join-accept is scheduled and removed each time an uplink is received from the device. | | `rx_windows_available` | [`bool`](#bool) | | Whether or not Rx windows are expected to be open. Set to true every time an uplink is received. Set to false every time a successful downlink scheduling attempt is made. | -| `recent_uplinks` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | repeated | Recent data uplink messages sorted by time. The number of messages stored may depend on configuration. | -| `recent_downlinks` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | repeated | Recent data downlink messages sorted by time. The number of messages stored may depend on configuration. | +| `recent_uplinks` | [`MACState.UplinkMessage`](#ttn.lorawan.v3.MACState.UplinkMessage) | repeated | Recent data uplink messages sorted by time. The number of messages stored may depend on configuration. | +| `recent_downlinks` | [`MACState.DownlinkMessage`](#ttn.lorawan.v3.MACState.DownlinkMessage) | repeated | Recent data downlink messages sorted by time. The number of messages stored may depend on configuration. | | `last_network_initiated_downlink_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Time when the last network-initiated downlink message was scheduled. | | `rejected_adr_data_rate_indexes` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | repeated | ADR Data rate index values rejected by the device. Reset each time `current_parameters.channels` change. Elements are sorted in ascending order. | | `rejected_adr_tx_power_indexes` | [`uint32`](#uint32) | repeated | ADR TX output power index values rejected by the device. Elements are sorted in ascending order. | | `rejected_frequencies` | [`uint64`](#uint64) | repeated | Frequencies rejected by the device. | | `last_downlink_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Time when the last downlink message was scheduled. | +| `rejected_data_rate_ranges` | [`MACState.RejectedDataRateRangesEntry`](#ttn.lorawan.v3.MACState.RejectedDataRateRangesEntry) | repeated | Data rate ranges rejected by the device per frequency. | +| `last_adr_change_f_cnt_up` | [`uint32`](#uint32) | | Frame counter of uplink, which confirmed the last ADR parameter change. | +| `recent_mac_command_identifiers` | [`MACCommandIdentifier`](#ttn.lorawan.v3.MACCommandIdentifier) | repeated | MAC command identifiers sent by the end device in the last received uplink. The Network Server may choose to store only certain types of MAC command identifiers in the underlying implementation. | #### Field Rules @@ -2351,14 +3818,96 @@ This is used internally by the Network Server and is read only. | `rejected_adr_tx_power_indexes` |

`repeated.max_items`: `15`

`repeated.items.uint32.lte`: `15`

| | `rejected_frequencies` |

`repeated.items.uint64.gte`: `100000`

| +### Message `MACState.DataRateRange` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `min_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | +| `max_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `min_data_rate_index` |

`enum.defined_only`: `true`

| +| `max_data_rate_index` |

`enum.defined_only`: `true`

| + +### Message `MACState.DataRateRanges` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ranges` | [`MACState.DataRateRange`](#ttn.lorawan.v3.MACState.DataRateRange) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `ranges` |

`repeated.min_items`: `1`

| + +### Message `MACState.DownlinkMessage` + +A minimal DownlinkMessage definition which is binary compatible with the top level DownlinkMessage message. +Used for type safe recent downlink storage. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `payload` | [`MACState.DownlinkMessage.Message`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message) | | | +| `correlation_ids` | [`string`](#string) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `correlation_ids` |

`repeated.items.string.max_len`: `100`

| + +### Message `MACState.DownlinkMessage.Message` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `m_hdr` | [`MACState.DownlinkMessage.Message.MHDR`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message.MHDR) | | | +| `mac_payload` | [`MACState.DownlinkMessage.Message.MACPayload`](#ttn.lorawan.v3.MACState.DownlinkMessage.Message.MACPayload) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `m_hdr` |

`message.required`: `true`

| + +### Message `MACState.DownlinkMessage.Message.MACPayload` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `f_port` | [`uint32`](#uint32) | | | +| `full_f_cnt` | [`uint32`](#uint32) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `f_port` |

`uint32.lte`: `255`

| + +### Message `MACState.DownlinkMessage.Message.MHDR` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `m_type` | [`MType`](#ttn.lorawan.v3.MType) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `m_type` |

`enum.defined_only`: `true`

| + ### Message `MACState.JoinAccept` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `payload` | [`bytes`](#bytes) | | Payload of the join-accept received from Join Server. | -| `request` | [`JoinRequest`](#ttn.lorawan.v3.JoinRequest) | | JoinRequest sent to Join Server. | +| `request` | [`MACState.JoinRequest`](#ttn.lorawan.v3.MACState.JoinRequest) | | | | `keys` | [`SessionKeys`](#ttn.lorawan.v3.SessionKeys) | | Network session keys associated with the join. | | `correlation_ids` | [`string`](#string) | repeated | | +| `dev_addr` | [`bytes`](#bytes) | | | +| `net_id` | [`bytes`](#bytes) | | | #### Field Rules @@ -2368,6 +3917,98 @@ This is used internally by the Network Server and is read only. | `request` |

`message.required`: `true`

| | `keys` |

`message.required`: `true`

| | `correlation_ids` |

`repeated.items.string.max_len`: `100`

| +| `dev_addr` |

`bytes.len`: `4`

| +| `net_id` |

`bytes.len`: `3`

| + +### Message `MACState.JoinRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `downlink_settings` | [`DLSettings`](#ttn.lorawan.v3.DLSettings) | | | +| `rx_delay` | [`RxDelay`](#ttn.lorawan.v3.RxDelay) | | | +| `cf_list` | [`CFList`](#ttn.lorawan.v3.CFList) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `downlink_settings` |

`message.required`: `true`

| +| `rx_delay` |

`enum.defined_only`: `true`

| + +### Message `MACState.RejectedDataRateRangesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`uint64`](#uint64) | | | +| `value` | [`MACState.DataRateRanges`](#ttn.lorawan.v3.MACState.DataRateRanges) | | | + +### Message `MACState.UplinkMessage` + +A minimal UplinkMessage definition which is binary compatible with the top level UplinkMessage message. +Used for type safe recent uplink storage. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `payload` | [`Message`](#ttn.lorawan.v3.Message) | | | +| `settings` | [`MACState.UplinkMessage.TxSettings`](#ttn.lorawan.v3.MACState.UplinkMessage.TxSettings) | | | +| `rx_metadata` | [`MACState.UplinkMessage.RxMetadata`](#ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata) | repeated | | +| `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `correlation_ids` | [`string`](#string) | repeated | | +| `device_channel_index` | [`uint32`](#uint32) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `payload` |

`message.required`: `true`

| +| `settings` |

`message.required`: `true`

| +| `correlation_ids` |

`repeated.items.string.max_len`: `100`

| +| `device_channel_index` |

`uint32.lte`: `255`

| + +### Message `MACState.UplinkMessage.RxMetadata` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `channel_rssi` | [`float`](#float) | | | +| `snr` | [`float`](#float) | | | +| `downlink_path_constraint` | [`DownlinkPathConstraint`](#ttn.lorawan.v3.DownlinkPathConstraint) | | | +| `uplink_token` | [`bytes`](#bytes) | | | +| `packet_broker` | [`MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata`](#ttn.lorawan.v3.MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`message.required`: `true`

| +| `downlink_path_constraint` |

`enum.defined_only`: `true`

| + +### Message `MACState.UplinkMessage.RxMetadata.PacketBrokerMetadata` + +### Message `MACState.UplinkMessage.TxSettings` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `data_rate` | [`DataRate`](#ttn.lorawan.v3.DataRate) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `data_rate` |

`message.required`: `true`

| + +### Message `ResetAndGetEndDeviceRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the end device fields that should be returned. See the API reference for which fields can be returned by the different services. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| ### Message `Session` @@ -2386,6 +4027,7 @@ This is used internally by the Network Server and is read only. | Field | Validations | | ----- | ----------- | +| `dev_addr` |

`bytes.len`: `4`

| | `keys` |

`message.required`: `true`

| ### Message `SetEndDeviceRequest` @@ -2428,14 +4070,30 @@ Power state of the device. ### Service `EndDeviceRegistry` +The EndDeviceRegistry service, exposed by the Identity Server, is used to manage +end device registrations. + +After registering an end device, it also needs to be registered in +the NsEndDeviceRegistry that is exposed by the Network Server, +the AsEndDeviceRegistry that is exposed by the Application Server, +and the JsEndDeviceRegistry that is exposed by the Join Server. + +Before deleting an end device it first needs to be deleted from the +NsEndDeviceRegistry, the AsEndDeviceRegistry and the JsEndDeviceRegistry. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `Create` | [`CreateEndDeviceRequest`](#ttn.lorawan.v3.CreateEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Create a new end device within an application. After creating a device in the EndDeviceRegistry (Identity Server), it still needs to be created in the NsEndDeviceRegistry, AsEndDeviceRegistry and JsEndDeviceRegistry. | +| `Create` | [`CreateEndDeviceRequest`](#ttn.lorawan.v3.CreateEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Create a new end device within an application. + +After registering an end device, it also needs to be registered in the NsEndDeviceRegistry that is exposed by the Network Server, the AsEndDeviceRegistry that is exposed by the Application Server, and the JsEndDeviceRegistry that is exposed by the Join Server. | | `Get` | [`GetEndDeviceRequest`](#ttn.lorawan.v3.GetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Get the end device with the given identifiers, selecting the fields specified in the field mask. More or less fields may be returned, depending on the rights of the caller. | | `GetIdentifiersForEUIs` | [`GetEndDeviceIdentifiersForEUIsRequest`](#ttn.lorawan.v3.GetEndDeviceIdentifiersForEUIsRequest) | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | Get the identifiers of the end device that has the given EUIs registered. | | `List` | [`ListEndDevicesRequest`](#ttn.lorawan.v3.ListEndDevicesRequest) | [`EndDevices`](#ttn.lorawan.v3.EndDevices) | List end devices in the given application. Similar to Get, this selects the fields given by the field mask. More or less fields may be returned, depending on the rights of the caller. | -| `Update` | [`UpdateEndDeviceRequest`](#ttn.lorawan.v3.UpdateEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Update the OAuth client, changing the fields specified by the field mask to the provided values. | -| `Delete` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the end device with the given IDs. Before deleting an end device from the EndDeviceRegistry (the Identity Server), the device needs to be deleted from the JsEndDeviceRegistry, AsEndDeviceRegistry and NsEndDeviceRegistry. This is NOT done automatically. | +| `Update` | [`UpdateEndDeviceRequest`](#ttn.lorawan.v3.UpdateEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Update the end device, changing the fields specified by the field mask to the provided values. | +| `BatchUpdateLastSeen` | [`BatchUpdateEndDeviceLastSeenRequest`](#ttn.lorawan.v3.BatchUpdateEndDeviceLastSeenRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Update the last seen timestamp for a batch of end devices. | +| `Delete` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the end device with the given IDs. + +Before deleting an end device it first needs to be deleted from the NsEndDeviceRegistry, the AsEndDeviceRegistry and the JsEndDeviceRegistry. This is NOT done automatically. | #### HTTP bindings @@ -2480,6 +4138,7 @@ Power state of the device. | `GATEWAY_CONFIGURATION_SERVER` | 10 | | | `QR_CODE_GENERATOR` | 11 | | | `PACKET_BROKER_AGENT` | 12 | | +| `DEVICE_REPOSITORY` | 13 | | ### Enum `DownlinkPathConstraint` @@ -2525,15 +4184,18 @@ The messages (for translation) are stored as "error::". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `name` | [`string`](#string) | | | -| `time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `identifiers` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | repeated | | -| `data` | [`google.protobuf.Any`](#google.protobuf.Any) | | | -| `correlation_ids` | [`string`](#string) | repeated | | -| `origin` | [`string`](#string) | | | -| `context` | [`Event.ContextEntry`](#ttn.lorawan.v3.Event.ContextEntry) | repeated | | +| `name` | [`string`](#string) | | Name of the event. This can be used to find the (localized) event description. | +| `time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Time at which the event was triggered. | +| `identifiers` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | repeated | Identifiers of the entity (or entities) involved. | +| `data` | [`google.protobuf.Any`](#google.protobuf.Any) | | Optional data attached to the event. | +| `correlation_ids` | [`string`](#string) | repeated | Correlation IDs can be used to find related events and actions such as API calls. | +| `origin` | [`string`](#string) | | The origin of the event. Typically the hostname of the server that created it. | +| `context` | [`Event.ContextEntry`](#ttn.lorawan.v3.Event.ContextEntry) | repeated | Event context, internal use only. | | `visibility` | [`Rights`](#ttn.lorawan.v3.Rights) | | The event will be visible to a caller that has any of these rights. | -| `authentication` | [`Event.Authentication`](#ttn.lorawan.v3.Event.Authentication) | | | +| `authentication` | [`Event.Authentication`](#ttn.lorawan.v3.Event.Authentication) | | Details on the authentication provided by the caller that triggered this event. | +| `remote_ip` | [`string`](#string) | | The IP address of the caller that triggered this event. | +| `user_agent` | [`string`](#string) | | The IP address of the caller that triggered this event. | +| `unique_id` | [`string`](#string) | | The unique identifier of the event, assigned on creation. | #### Field Rules @@ -2546,10 +4208,9 @@ The messages (for translation) are stored as "error::". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `type` | [`string`](#string) | | | -| `token_type` | [`string`](#string) | | | -| `token_id` | [`string`](#string) | | | -| `remote_ip` | [`string`](#string) | | | +| `type` | [`string`](#string) | | The type of authentication that was used. This is typically a bearer token. | +| `token_type` | [`string`](#string) | | The type of token that was used. Common types are APIKey, AccessToken and SessionToken. | +| `token_id` | [`string`](#string) | | The ID of the token that was used. | ### Message `Event.ContextEntry` @@ -2558,6 +4219,24 @@ The messages (for translation) are stored as "error::". | `key` | [`string`](#string) | | | | `value` | [`bytes`](#bytes) | | | +### Message `FindRelatedEventsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `correlation_id` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `correlation_id` |

`string.min_len`: `1`

`string.max_len`: `100`

| + +### Message `FindRelatedEventsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `events` | [`Event`](#ttn.lorawan.v3.Event) | repeated | | + ### Message `StreamEventsRequest` | Field | Type | Label | Description | @@ -2565,6 +4244,7 @@ The messages (for translation) are stored as "error::". | `identifiers` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | repeated | | | `tail` | [`uint32`](#uint32) | | If greater than zero, this will return historical events, up to this maximum when the stream starts. If used in combination with "after", the limit that is reached first, is used. The availability of historical events depends on server support and retention policy. | | `after` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | If not empty, this will return historical events after the given time when the stream starts. If used in combination with "tail", the limit that is reached first, is used. The availability of historical events depends on server support and retention policy. | +| `names` | [`string`](#string) | repeated | If provided, this will filter events, so that only events with the given names are returned. Names can be provided as either exact event names (e.g. 'gs.up.receive'), or as regular expressions (e.g. '/^gs\..+/'). | ### Service `Events` @@ -2573,12 +4253,14 @@ The Events service serves events from the cluster. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `Stream` | [`StreamEventsRequest`](#ttn.lorawan.v3.StreamEventsRequest) | [`Event`](#ttn.lorawan.v3.Event) _stream_ | Stream live events, optionally with a tail of historical events (depending on server support and retention policy). Events may arrive out-of-order. | +| `FindRelated` | [`FindRelatedEventsRequest`](#ttn.lorawan.v3.FindRelatedEventsRequest) | [`FindRelatedEventsResponse`](#ttn.lorawan.v3.FindRelatedEventsResponse) | | #### HTTP bindings | Method Name | Method | Pattern | Body | | ----------- | ------ | ------- | ---- | | `Stream` | `POST` | `/api/v3/events` | `*` | +| `FindRelated` | `GET` | `/api/v3/events/related` | | ## File `lorawan-stack/api/gateway.proto` @@ -2589,6 +4271,7 @@ The Events service serves events from the cluster. | `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | | `name` | [`string`](#string) | | | | `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | #### Field Rules @@ -2596,7 +4279,8 @@ The Events service serves events from the cluster. | ----- | ----------- | | `gateway_ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| -| `rights` |

`repeated.items.enum.defined_only`: `true`

| +| `rights` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `expires_at` |

`timestamp.gt_now`: `true`

| ### Message `CreateGatewayRequest` @@ -2618,27 +4302,37 @@ Gateway is the message that defines a gateway on the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `name` | [`string`](#string) | | | -| `description` | [`string`](#string) | | | +| `ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | The identifiers of the gateway. These are public and can be seen by any authenticated user in the network. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the gateway was created. This information is public and can be seen by any authenticated user in the network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the gateway was last updated. This information is public and can be seen by any authenticated user in the network. | +| `deleted_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the gateway was deleted. This information is public and can be seen by any authenticated user in the network. | +| `name` | [`string`](#string) | | The name of the gateway. This information is public and can be seen by any authenticated user in the network. | +| `description` | [`string`](#string) | | A description for the gateway. This information is public and can be seen by any authenticated user in the network. | | `attributes` | [`Gateway.AttributesEntry`](#ttn.lorawan.v3.Gateway.AttributesEntry) | repeated | Key-value attributes for this gateway. Typically used for organizing gateways or for storing integration-specific data. | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway. This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `technical_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | | `version_ids` | [`GatewayVersionIdentifiers`](#ttn.lorawan.v3.GatewayVersionIdentifiers) | | | -| `gateway_server_address` | [`string`](#string) | | The address of the Gateway Server to connect to. The typical format of the address is "host:port". If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. The connection shall be established with transport layer security (TLS). Custom certificate authorities may be configured out-of-band. | +| `gateway_server_address` | [`string`](#string) | | The address of the Gateway Server to connect to. This information is public and can be seen by any authenticated user in the network if status_public is true. The typical format of the address is "scheme://host:port". The scheme is optional. If the port is omitted, the normal port inference (with DNS lookup, otherwise defaults) is used. The connection shall be established with transport layer security (TLS). Custom certificate authorities may be configured out-of-band. | | `auto_update` | [`bool`](#bool) | | | | `update_channel` | [`string`](#string) | | | -| `frequency_plan_id` | [`string`](#string) | | Frequency plan ID of the gateway. This equals the first element of the frequency_plan_ids field. | -| `frequency_plan_ids` | [`string`](#string) | repeated | Frequency plan IDs of the gateway. The first element equals the frequency_plan_id field. | -| `antennas` | [`GatewayAntenna`](#ttn.lorawan.v3.GatewayAntenna) | repeated | | +| `frequency_plan_id` | [`string`](#string) | | Frequency plan ID of the gateway. This information is public and can be seen by any authenticated user in the network. DEPRECATED: use frequency_plan_ids. This equals the first element of the frequency_plan_ids field. | +| `frequency_plan_ids` | [`string`](#string) | repeated | Frequency plan IDs of the gateway. This information is public and can be seen by any authenticated user in the network. The first element equals the frequency_plan_id field. | +| `antennas` | [`GatewayAntenna`](#ttn.lorawan.v3.GatewayAntenna) | repeated | Antennas of the gateway. Location information of the antennas is public and can be seen by any authenticated user in the network if location_public=true. | | `status_public` | [`bool`](#bool) | | The status of this gateway may be publicly displayed. | | `location_public` | [`bool`](#bool) | | The location of this gateway may be publicly displayed. | | `schedule_downlink_late` | [`bool`](#bool) | | Enable server-side buffering of downlink messages. This is recommended for gateways using the Semtech UDP Packet Forwarder v2.x or older, as it does not feature a just-in-time queue. If enabled, the Gateway Server schedules the downlink message late to the gateway so that it does not overwrite previously scheduled downlink messages that have not been transmitted yet. | | `enforce_duty_cycle` | [`bool`](#bool) | | Enforcing gateway duty cycle is recommended for all gateways to respect spectrum regulations. Disable enforcing the duty cycle only in controlled research and development environments. | | `downlink_path_constraint` | [`DownlinkPathConstraint`](#ttn.lorawan.v3.DownlinkPathConstraint) | | | | `schedule_anytime_delay` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Adjust the time that GS schedules class C messages in advance. This is useful for gateways that have a known high latency backhaul, like 3G and satellite. | -| `update_location_from_status` | [`bool`](#bool) | | update the location of this gateway from status messages | +| `update_location_from_status` | [`bool`](#bool) | | Update the location of this gateway from status messages. This only works for gateways connecting with authentication; gateways connected over UDP are not supported. | +| `lbs_lns_secret` | [`Secret`](#ttn.lorawan.v3.Secret) | | The LoRa Basics Station LNS secret. This is either an auth token (such as an API Key) or a TLS private certificate. Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. | +| `claim_authentication_code` | [`GatewayClaimAuthenticationCode`](#ttn.lorawan.v3.GatewayClaimAuthenticationCode) | | The authentication code for gateway claiming. Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. The entire field must be used in RPCs since sub-fields are validated wrt to each other. Direct selection/update of sub-fields only are not allowed. Use the top level field mask `claim_authentication_code` even when updating single fields. | +| `target_cups_uri` | [`string`](#string) | | CUPS URI for LoRa Basics Station CUPS redirection. The CUPS Trust field will be automatically fetched from the cert chain presented by the target server. | +| `target_cups_key` | [`Secret`](#ttn.lorawan.v3.Secret) | | CUPS Key for LoRa Basics Station CUPS redirection. If redirecting to another instance of TTS, use the CUPS API Key for the gateway on the target instance. Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. | +| `require_authenticated_connection` | [`bool`](#bool) | | Require an authenticated gateway connection. This prevents the gateway from using the UDP protocol and requires authentication when using other protocols. | +| `lrfhss` | [`Gateway.LRFHSS`](#ttn.lorawan.v3.Gateway.LRFHSS) | | | +| `disable_packet_broker_forwarding` | [`bool`](#bool) | | | #### Field Rules @@ -2647,12 +4341,15 @@ Gateway is the message that defines a gateway on the network. | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| -| `version_ids` |

`message.required`: `true`

| -| `gateway_server_address` |

`string.pattern`: `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| +| `contact_info` |

`repeated.max_items`: `10`

| +| `gateway_server_address` |

`string.pattern`: `^([a-z]{2,5}://)?(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$`

| +| `update_channel` |

`string.max_len`: `128`

| | `frequency_plan_id` |

`string.max_len`: `64`

| | `frequency_plan_ids` |

`repeated.max_items`: `8`

`repeated.items.string.max_len`: `64`

| +| `antennas` |

`repeated.max_items`: `8`

| | `downlink_path_constraint` |

`enum.defined_only`: `true`

| +| `target_cups_uri` |

`string.uri`: `true`

| ### Message `Gateway.AttributesEntry` @@ -2661,22 +4358,30 @@ Gateway is the message that defines a gateway on the network. | `key` | [`string`](#string) | | | | `value` | [`string`](#string) | | | +### Message `Gateway.LRFHSS` + +LR-FHSS gateway capabilities. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `supported` | [`bool`](#bool) | | The gateway supports the LR-FHSS uplink channels. | + ### Message `GatewayAntenna` GatewayAntenna is the message that defines a gateway antenna. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `gain` | [`float`](#float) | | gain is the antenna gain relative to this gateway, in dBi. | +| `gain` | [`float`](#float) | | Antenna gain relative to the gateway, in dBi. | | `location` | [`Location`](#ttn.lorawan.v3.Location) | | location is the antenna's location. | | `attributes` | [`GatewayAntenna.AttributesEntry`](#ttn.lorawan.v3.GatewayAntenna.AttributesEntry) | repeated | | +| `placement` | [`GatewayAntennaPlacement`](#ttn.lorawan.v3.GatewayAntennaPlacement) | | | #### Field Rules | Field | Validations | | ----- | ----------- | -| `location` |

`message.required`: `true`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| ### Message `GatewayAntenna.AttributesEntry` @@ -2694,6 +4399,16 @@ GatewayAntenna is the message that defines a gateway antenna. | `url` | [`string`](#string) | | | | `logos` | [`string`](#string) | repeated | Logos contains file names of brand logos. | +### Message `GatewayClaimAuthenticationCode` + +Authentication code for claiming gateways. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `secret` | [`Secret`](#ttn.lorawan.v3.Secret) | | | +| `valid_from` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `valid_to` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | + ### Message `GatewayConnectionStats` Connection stats as monitored by the Gateway Server. @@ -2701,6 +4416,7 @@ Connection stats as monitored by the Gateway Server. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `connected_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `disconnected_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | | `protocol` | [`string`](#string) | | Protocol used to connect (for example, udp, mqtt, grpc) | | `last_status_received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | | `last_status` | [`GatewayStatus`](#ttn.lorawan.v3.GatewayStatus) | | | @@ -2710,6 +4426,7 @@ Connection stats as monitored by the Gateway Server. | `downlink_count` | [`uint64`](#uint64) | | | | `round_trip_times` | [`GatewayConnectionStats.RoundTripTimes`](#ttn.lorawan.v3.GatewayConnectionStats.RoundTripTimes) | | | | `sub_bands` | [`GatewayConnectionStats.SubBand`](#ttn.lorawan.v3.GatewayConnectionStats.SubBand) | repeated | Statistics for each sub band. | +| `gateway_remote_address` | [`GatewayRemoteAddress`](#ttn.lorawan.v3.GatewayRemoteAddress) | | Gateway Remote Address. | ### Message `GatewayConnectionStats.RoundTripTimes` @@ -2734,8 +4451,8 @@ Connection stats as monitored by the Gateway Server. | ----- | ---- | ----- | ----------- | | `min_frequency` | [`uint64`](#uint64) | | | | `max_frequency` | [`uint64`](#uint64) | | | -| `downlink_utilization_limit` | [`float`](#float) | | | -| `downlink_utilization` | [`float`](#float) | | | +| `downlink_utilization_limit` | [`float`](#float) | | Duty-cycle limit of the sub-band as a fraction of time. | +| `downlink_utilization` | [`float`](#float) | | Utilization rate of the available duty-cycle. This value should not exceed downlink_utilization_limit. | ### Message `GatewayModel` @@ -2770,6 +4487,14 @@ Connection stats as monitored by the Gateway Server. | `max_frequency` | [`uint64`](#uint64) | | | | `notch_frequency` | [`uint64`](#uint64) | | | +### Message `GatewayRemoteAddress` + +Remote Address of the Gateway, as seen by the Gateway Server. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ip` | [`string`](#string) | | IPv4 or IPv6 address. | + ### Message `GatewayStatus` | Field | Type | Label | Description | @@ -2786,9 +4511,10 @@ Connection stats as monitored by the Gateway Server. | Field | Validations | | ----- | ----------- | -| `time` |

`timestamp.required`: `true`

| -| `versions` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[_-]?[a-z0-9]){2,}$`

| -| `metrics` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[_-]?[a-z0-9]){2,}$`

| +| `versions` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[_-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `128`

| +| `antenna_locations` |

`repeated.max_items`: `8`

| +| `ip` |

`repeated.max_items`: `10`

`repeated.items.string.ip`: `true`

| +| `metrics` |

`map.max_pairs`: `32`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[_-]?[a-z0-9]){2,}$`

| ### Message `GatewayStatus.MetricsEntry` @@ -2804,23 +4530,6 @@ Connection stats as monitored by the Gateway Server. | `key` | [`string`](#string) | | | | `value` | [`string`](#string) | | | -### Message `GatewayVersion` - -Template for creating gateways. - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `ids` | [`GatewayVersionIdentifiers`](#ttn.lorawan.v3.GatewayVersionIdentifiers) | | Version identifiers. | -| `photos` | [`string`](#string) | repeated | Photos contains file names of gateway photos. | -| `radios` | [`GatewayRadio`](#ttn.lorawan.v3.GatewayRadio) | repeated | | -| `clock_source` | [`uint32`](#uint32) | | | - -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `ids` |

`message.required`: `true`

| - ### Message `GatewayVersionIdentifiers` Identifies an end device model with version information. @@ -2838,6 +4547,8 @@ Identifies an end device model with version information. | ----- | ----------- | | `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| | `model_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `hardware_version` |

`string.max_len`: `32`

| +| `firmware_version` |

`string.max_len`: `32`

| ### Message `Gateways` @@ -2878,6 +4589,12 @@ Identifies an end device model with version information. | ----- | ---- | ----- | ----------- | | `eui` | [`bytes`](#bytes) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `eui` |

`bytes.len`: `8`

| + ### Message `GetGatewayRequest` | Field | Type | Label | Description | @@ -2896,6 +4613,7 @@ Identifies an end device model with version information. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `order` | [`string`](#string) | | Order the results by this field path. Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | @@ -2904,6 +4622,7 @@ Identifies an end device model with version information. | Field | Validations | | ----- | ----------- | | `gateway_ids` |

`message.required`: `true`

| +| `order` |

`string.in`: `[ api_key_id -api_key_id name -name created_at -created_at expires_at -expires_at]`

| | `limit` |

`uint32.lte`: `1000`

| ### Message `ListGatewayCollaboratorsRequest` @@ -2913,6 +4632,7 @@ Identifies an end device model with version information. | `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | #### Field Rules @@ -2920,6 +4640,7 @@ Identifies an end device model with version information. | ----- | ----------- | | `gateway_ids` |

`message.required`: `true`

| | `limit` |

`uint32.lte`: `1000`

| +| `order` |

`string.in`: `[ id -id -rights rights]`

| ### Message `ListGatewaysRequest` @@ -2930,6 +4651,7 @@ Identifies an end device model with version information. | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted gateways. | #### Field Rules @@ -2949,35 +4671,84 @@ Identifies an end device model with version information. | Field | Validations | | ----- | ----------- | -| `gateway_ids` |

`message.required`: `true`

| -| `collaborator` |

`message.required`: `true`

| +| `gateway_ids` |

`message.required`: `true`

| +| `collaborator` |

`message.required`: `true`

| + +### Message `UpdateGatewayAPIKeyRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the api key fields that should be updated. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`message.required`: `true`

| +| `api_key` |

`message.required`: `true`

| + +### Message `UpdateGatewayRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway` | [`Gateway`](#ttn.lorawan.v3.Gateway) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the gateway fields that should be updated. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway` |

`message.required`: `true`

| -### Message `UpdateGatewayAPIKeyRequest` +### Enum `GatewayAntennaPlacement` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `PLACEMENT_UNKNOWN` | 0 | | +| `INDOOR` | 1 | | +| `OUTDOOR` | 2 | | + +## File `lorawan-stack/api/gateway_configuration.proto` + +### Message `GetGatewayConfigurationRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | -| `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `format` | [`string`](#string) | | | +| `type` | [`string`](#string) | | | +| `filename` | [`string`](#string) | | | #### Field Rules | Field | Validations | | ----- | ----------- | | `gateway_ids` |

`message.required`: `true`

| -| `api_key` |

`message.required`: `true`

| +| `format` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `type` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `filename` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-._]?[a-z0-9]){2,}$|^$`

| -### Message `UpdateGatewayRequest` +### Message `GetGatewayConfigurationResponse` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `gateway` | [`Gateway`](#ttn.lorawan.v3.Gateway) | | | -| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the gateway fields that should be updated. | +| `contents` | [`bytes`](#bytes) | | | -#### Field Rules +### Service `GatewayConfigurationService` -| Field | Validations | -| ----- | ----------- | -| `gateway` |

`message.required`: `true`

| +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `GetGatewayConfiguration` | [`GetGatewayConfigurationRequest`](#ttn.lorawan.v3.GetGatewayConfigurationRequest) | [`GetGatewayConfigurationResponse`](#ttn.lorawan.v3.GetGatewayConfigurationResponse) | | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `GetGatewayConfiguration` | `` | `/api/v3` | | +| `GetGatewayConfiguration` | `GET` | `/api/v3/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{filename}` | | +| `GetGatewayConfiguration` | `GET` | `/api/v3/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{type}/{filename}` | | ## File `lorawan-stack/api/gateway_services.proto` @@ -2990,6 +4761,9 @@ Identifies an end device model with version information. ### Service `GatewayAccess` +The GatewayAcces service, exposed by the Identity Server, is used to manage +API keys and collaborators of gateways. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `ListRights` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this gateway. | @@ -3024,6 +4798,9 @@ Identifies an end device model with version information. ### Service `GatewayRegistry` +The GatewayRegistry service, exposed by the Identity Server, is used to manage +gateway registrations. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `Create` | [`CreateGatewayRequest`](#ttn.lorawan.v3.CreateGatewayRequest) | [`Gateway`](#ttn.lorawan.v3.Gateway) | Create a new gateway. This also sets the given organization or user as first collaborator with all possible rights. | @@ -3032,6 +4809,10 @@ Identifies an end device model with version information. | `List` | [`ListGatewaysRequest`](#ttn.lorawan.v3.ListGatewaysRequest) | [`Gateways`](#ttn.lorawan.v3.Gateways) | List gateways where the given user or organization is a direct collaborator. If no user or organization is given, this returns the gateways the caller has access to. Similar to Get, this selects the fields given by the field mask. More or less fields may be returned, depending on the rights of the caller. | | `Update` | [`UpdateGatewayRequest`](#ttn.lorawan.v3.UpdateGatewayRequest) | [`Gateway`](#ttn.lorawan.v3.Gateway) | Update the gateway, changing the fields specified by the field mask to the provided values. | | `Delete` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the gateway. This may not release the gateway ID for reuse, but it does release the EUI. | +| `Restore` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Restore a recently deleted gateway. This does not restore the EUI, as that was released when deleting the gateway. + +Deployment configuration may specify if, and for how long after deletion, entities can be restored. | +| `Purge` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Purge the gateway. This will release both gateway ID and EUI for reuse. The gateway owner is responsible for clearing data from any (external) integrations that may store and expose data by gateway ID. | #### HTTP bindings @@ -3045,9 +4826,37 @@ Identifies an end device model with version information. | `List` | `GET` | `/api/v3/organizations/{collaborator.organization_ids.organization_id}/gateways` | | | `Update` | `PUT` | `/api/v3/gateways/{gateway.ids.gateway_id}` | `*` | | `Delete` | `DELETE` | `/api/v3/gateways/{gateway_id}` | | +| `Restore` | `POST` | `/api/v3/gateways/{gateway_id}/restore` | | +| `Purge` | `DELETE` | `/api/v3/gateways/{gateway_id}/purge` | | ## File `lorawan-stack/api/gatewayserver.proto` +### Message `BatchGetGatewayConnectionStatsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | repeated | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the gateway stats fields that should be returned. This mask will be applied on each entry returned. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`repeated.min_items`: `1`

`repeated.max_items`: `100`

| + +### Message `BatchGetGatewayConnectionStatsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `entries` | [`BatchGetGatewayConnectionStatsResponse.EntriesEntry`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsResponse.EntriesEntry) | repeated | The map key is the gateway identifier. | + +### Message `BatchGetGatewayConnectionStatsResponse.EntriesEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`GatewayConnectionStats`](#ttn.lorawan.v3.GatewayConnectionStats) | | | + ### Message `GatewayDown` GatewayDown contains downlink messages for the gateway. @@ -3062,21 +4871,24 @@ GatewayUp may contain zero or more uplink messages and/or a status message for t | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `uplink_messages` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | repeated | UplinkMessages received by the gateway. | -| `gateway_status` | [`GatewayStatus`](#ttn.lorawan.v3.GatewayStatus) | | | -| `tx_acknowledgment` | [`TxAcknowledgment`](#ttn.lorawan.v3.TxAcknowledgment) | | | +| `uplink_messages` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | repeated | Uplink messages received by the gateway. | +| `gateway_status` | [`GatewayStatus`](#ttn.lorawan.v3.GatewayStatus) | | Gateway status produced by the gateway. | +| `tx_acknowledgment` | [`TxAcknowledgment`](#ttn.lorawan.v3.TxAcknowledgment) | | A Tx acknowledgment or error. | ### Message `ScheduleDownlinkErrorDetails` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `path_errors` | [`ErrorDetails`](#ttn.lorawan.v3.ErrorDetails) | repeated | | +| `path_errors` | [`ErrorDetails`](#ttn.lorawan.v3.ErrorDetails) | repeated | Errors per path when downlink scheduling failed. | ### Message `ScheduleDownlinkResponse` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `delay` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | | +| `delay` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | The amount of time between the message has been scheduled and it will be transmitted by the gateway. | +| `downlink_path` | [`DownlinkPath`](#ttn.lorawan.v3.DownlinkPath) | | Downlink path chosen by the Gateway Server. | +| `rx1` | [`bool`](#bool) | | Whether RX1 has been chosen for the downlink message. Both RX1 and RX2 can be used for transmitting the same message by the same gateway. | +| `rx2` | [`bool`](#bool) | | Whether RX2 has been chosen for the downlink message. Both RX1 and RX2 can be used for transmitting the same message by the same gateway. | #### Field Rules @@ -3089,12 +4901,14 @@ GatewayUp may contain zero or more uplink messages and/or a status message for t | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `GetGatewayConnectionStats` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`GatewayConnectionStats`](#ttn.lorawan.v3.GatewayConnectionStats) | Get statistics about the current gateway connection to the Gateway Server. This is not persisted between reconnects. | +| `BatchGetGatewayConnectionStats` | [`BatchGetGatewayConnectionStatsRequest`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsRequest) | [`BatchGetGatewayConnectionStatsResponse`](#ttn.lorawan.v3.BatchGetGatewayConnectionStatsResponse) | Get statistics about gateway connections to the Gateway Server of a batch of gateways. This is not persisted between reconnects. Gateways that are not connected or are part of a different cluster are ignored. It is up to the client to make sure that the gateways are in the requested cluster. | #### HTTP bindings | Method Name | Method | Pattern | Body | | ----------- | ------ | ------- | ---- | | `GetGatewayConnectionStats` | `GET` | `/api/v3/gs/gateways/{gateway_id}/connection/stats` | | +| `BatchGetGatewayConnectionStats` | `POST` | `/api/v3/gs/gateways/connection/stats` | `*` | ### Service `GtwGs` @@ -3102,10 +4916,10 @@ The GtwGs service connects a gateway to a Gateway Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `LinkGateway` | [`GatewayUp`](#ttn.lorawan.v3.GatewayUp) _stream_ | [`GatewayDown`](#ttn.lorawan.v3.GatewayDown) _stream_ | Link the gateway to the Gateway Server. | -| `GetConcentratorConfig` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`ConcentratorConfig`](#ttn.lorawan.v3.ConcentratorConfig) | GetConcentratorConfig associated to the gateway. | -| `GetMQTTConnectionInfo` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | Get the MQTT server address and the username for the gateway. | -| `GetMQTTV2ConnectionInfo` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | Get the MQTTV2 server address and the username for the gateway. | +| `LinkGateway` | [`GatewayUp`](#ttn.lorawan.v3.GatewayUp) _stream_ | [`GatewayDown`](#ttn.lorawan.v3.GatewayDown) _stream_ | Link a gateway to the Gateway Server for streaming upstream messages and downstream messages. | +| `GetConcentratorConfig` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`ConcentratorConfig`](#ttn.lorawan.v3.ConcentratorConfig) | Get configuration for the concentrator. | +| `GetMQTTConnectionInfo` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | Get connection information to connect an MQTT gateway. | +| `GetMQTTV2ConnectionInfo` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | [`MQTTConnectionInfo`](#ttn.lorawan.v3.MQTTConnectionInfo) | Get legacy connection information to connect a The Things Network Stack V2 MQTT gateway. | #### HTTP bindings @@ -3120,7 +4934,7 @@ The NsGs service connects a Network Server to a Gateway Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `ScheduleDownlink` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | [`ScheduleDownlinkResponse`](#ttn.lorawan.v3.ScheduleDownlinkResponse) | ScheduleDownlink instructs the Gateway Server to schedule a downlink message. The Gateway Server may refuse if there are any conflicts in the schedule or if a duty cycle prevents the gateway from transmitting. | +| `ScheduleDownlink` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | [`ScheduleDownlinkResponse`](#ttn.lorawan.v3.ScheduleDownlinkResponse) | Instructs the Gateway Server to schedule a downlink message. The Gateway Server may refuse if there are any conflicts in the schedule or if a duty cycle prevents the gateway from transmitting. | ## File `lorawan-stack/api/identifiers.proto` @@ -3148,15 +4962,6 @@ The NsGs service connects a Network Server to a Gateway Server. | ----- | ----------- | | `client_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| -### Message `CombinedIdentifiers` - -Combine the identifiers of multiple entities. -The main purpose of this message is its use in events. - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `entity_identifiers` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | repeated | | - ### Message `EndDeviceIdentifiers` | Field | Type | Label | Description | @@ -3173,6 +4978,35 @@ The main purpose of this message is its use in events. | ----- | ----------- | | `device_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| | `application_ids` |

`message.required`: `true`

| +| `dev_eui` |

`bytes.len`: `8`

| +| `join_eui` |

`bytes.len`: `8`

| +| `dev_addr` |

`bytes.len`: `4`

| + +### Message `EndDeviceVersionIdentifiers` + +Identifies an end device model with version information. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `brand_id` | [`string`](#string) | | | +| `model_id` | [`string`](#string) | | | +| `hardware_version` | [`string`](#string) | | | +| `firmware_version` | [`string`](#string) | | | +| `band_id` | [`string`](#string) | | | +| `vendor_id` | [`uint32`](#uint32) | | VendorID managed by the LoRa Alliance, as defined in TR005. | +| `vendor_profile_id` | [`uint32`](#uint32) | | ID of the LoRaWAN end device profile assigned by the vendor. | +| `serial_number` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `brand_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `model_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `hardware_version` |

`string.max_len`: `32`

| +| `firmware_version` |

`string.max_len`: `32`

| +| `band_id` |

`string.max_len`: `32`

| +| `serial_number` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| ### Message `EntityIdentifiers` @@ -3199,6 +5033,29 @@ EntityIdentifiers contains one of the possible entity identifiers. | Field | Validations | | ----- | ----------- | | `gateway_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `eui` |

`bytes.len`: `8`

| + +### Message `NetworkIdentifiers` + +Identifies a Network Server. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `net_id` | [`bytes`](#bytes) | | LoRa Alliance NetID. | +| `tenant_id` | [`string`](#string) | | Optional tenant identifier for multi-tenant deployments. | +| `cluster_id` | [`string`](#string) | | Cluster identifier of the Network Server. | +| `cluster_address` | [`string`](#string) | | Cluster address of the Network Server. | +| `tenant_address` | [`string`](#string) | | Optional tenant address for multi-tenant deployments. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `net_id` |

`bytes.len`: `3`

| +| `tenant_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `cluster_id` |

`string.max_len`: `64`

| +| `cluster_address` |

`string.max_len`: `256`

| +| `tenant_address` |

`string.max_len`: `256`

| ### Message `OrganizationIdentifiers` @@ -3232,7 +5089,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | Field | Validations | | ----- | ----------- | -| `user_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `user_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){1,}$`

| ## File `lorawan-stack/api/identityserver.proto` @@ -3243,6 +5100,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `api_key` | [`AuthInfoResponse.APIKeyAccess`](#ttn.lorawan.v3.AuthInfoResponse.APIKeyAccess) | | | | `oauth_access_token` | [`OAuthAccessToken`](#ttn.lorawan.v3.OAuthAccessToken) | | | | `user_session` | [`UserSession`](#ttn.lorawan.v3.UserSession) | | Warning: A user authorized by session cookie will be granted all current and future rights. When using this auth type, the respective handlers need to ensure thorough CSRF and CORS protection using appropriate middleware. | +| `gateway_token` | [`AuthInfoResponse.GatewayToken`](#ttn.lorawan.v3.AuthInfoResponse.GatewayToken) | | | | `universal_rights` | [`Rights`](#ttn.lorawan.v3.Rights) | | | | `is_admin` | [`bool`](#bool) | | | @@ -3260,6 +5118,19 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `api_key` |

`message.required`: `true`

| | `entity_ids` |

`message.required`: `true`

| +### Message `AuthInfoResponse.GatewayToken` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`message.required`: `true`

| + ### Message `GetIsConfigurationRequest` ### Message `GetIsConfigurationResponse` @@ -3276,6 +5147,14 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `profile_picture` | [`IsConfiguration.ProfilePicture`](#ttn.lorawan.v3.IsConfiguration.ProfilePicture) | | | | `end_device_picture` | [`IsConfiguration.EndDevicePicture`](#ttn.lorawan.v3.IsConfiguration.EndDevicePicture) | | | | `user_rights` | [`IsConfiguration.UserRights`](#ttn.lorawan.v3.IsConfiguration.UserRights) | | | +| `user_login` | [`IsConfiguration.UserLogin`](#ttn.lorawan.v3.IsConfiguration.UserLogin) | | | +| `admin_rights` | [`IsConfiguration.AdminRights`](#ttn.lorawan.v3.IsConfiguration.AdminRights) | | | + +### Message `IsConfiguration.AdminRights` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `all` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | ### Message `IsConfiguration.EndDevicePicture` @@ -3290,6 +5169,12 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `disable_upload` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | | `use_gravatar` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | +### Message `IsConfiguration.UserLogin` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `disable_credentials_login` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | | + ### Message `IsConfiguration.UserRegistration` | Field | Type | Label | Description | @@ -3298,6 +5183,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `contact_info_validation` | [`IsConfiguration.UserRegistration.ContactInfoValidation`](#ttn.lorawan.v3.IsConfiguration.UserRegistration.ContactInfoValidation) | | | | `admin_approval` | [`IsConfiguration.UserRegistration.AdminApproval`](#ttn.lorawan.v3.IsConfiguration.UserRegistration.AdminApproval) | | | | `password_requirements` | [`IsConfiguration.UserRegistration.PasswordRequirements`](#ttn.lorawan.v3.IsConfiguration.UserRegistration.PasswordRequirements) | | | +| `enabled` | [`bool`](#bool) | | | ### Message `IsConfiguration.UserRegistration.AdminApproval` @@ -3353,7 +5239,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `GetConfiguration` | [`GetIsConfigurationRequest`](#ttn.lorawan.v3.GetIsConfigurationRequest) | [`GetIsConfigurationResponse`](#ttn.lorawan.v3.GetIsConfigurationResponse) | | +| `GetConfiguration` | [`GetIsConfigurationRequest`](#ttn.lorawan.v3.GetIsConfigurationRequest) | [`GetIsConfigurationResponse`](#ttn.lorawan.v3.GetIsConfigurationResponse) | Get the configuration of the Identity Server. The response is typically used to enable or disable features in a user interface. | #### HTTP bindings @@ -3376,12 +5262,15 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `rx_delay` | [`RxDelay`](#ttn.lorawan.v3.RxDelay) | | | | `cf_list` | [`CFList`](#ttn.lorawan.v3.CFList) | | Optional CFList. | | `correlation_ids` | [`string`](#string) | repeated | | +| `consumed_airtime` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Consumed airtime for the transmission of the join request. Calculated by Network Server using the RawPayload size and the transmission settings. | #### Field Rules | Field | Validations | | ----- | ----------- | | `raw_payload` |

`bytes.len`: `23`

| +| `dev_addr` |

`bytes.len`: `4`

| +| `net_id` |

`bytes.len`: `3`

| | `downlink_settings` |

`message.required`: `true`

| | `rx_delay` |

`enum.defined_only`: `true`

| | `correlation_ids` |

`repeated.items.string.max_len`: `100`

| @@ -3417,15 +5306,32 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ----------- | | `app_s_key` |

`message.required`: `true`

| +### Message `ApplicationActivationSettings` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `kek_label` | [`string`](#string) | | The KEK label to use for wrapping application keys. | +| `kek` | [`KeyEnvelope`](#ttn.lorawan.v3.KeyEnvelope) | | The (encrypted) Key Encryption Key. | +| `home_net_id` | [`bytes`](#bytes) | | Home NetID. | +| `application_server_id` | [`string`](#string) | | The AS-ID of the Application Server to use. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `kek_label` |

`string.max_len`: `2048`

| +| `home_net_id` |

`bytes.len`: `3`

| +| `application_server_id` |

`string.max_len`: `100`

| + ### Message `CryptoServicePayloadRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | -| `lorawan_version` | [`MACVersion`](#ttn.lorawan.v3.MACVersion) | | | -| `payload` | [`bytes`](#bytes) | | | -| `provisioner_id` | [`string`](#string) | | | -| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | End device identifiers for the cryptographic operation. | +| `lorawan_version` | [`MACVersion`](#ttn.lorawan.v3.MACVersion) | | LoRaWAN version to use for the cryptographic operation. | +| `payload` | [`bytes`](#bytes) | | Raw input payload. | +| `provisioner_id` | [`string`](#string) | | Provisioner that provisioned the end device. | +| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Provisioning data for the provisioner. | #### Field Rules @@ -3433,25 +5339,38 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ----------- | | `ids` |

`message.required`: `true`

| | `lorawan_version` |

`enum.defined_only`: `true`

| +| `payload` |

`bytes.max_len`: `256`

| | `provisioner_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| ### Message `CryptoServicePayloadResponse` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `payload` | [`bytes`](#bytes) | | | +| `payload` | [`bytes`](#bytes) | | Raw output payload. | + +### Message `DeleteApplicationActivationSettingsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| ### Message `DeriveSessionKeysRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | -| `lorawan_version` | [`MACVersion`](#ttn.lorawan.v3.MACVersion) | | | -| `join_nonce` | [`bytes`](#bytes) | | | -| `dev_nonce` | [`bytes`](#bytes) | | | -| `net_id` | [`bytes`](#bytes) | | | -| `provisioner_id` | [`string`](#string) | | | -| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | End device identifiers to use for key derivation. The DevAddr must be set in this request. The DevEUI may need to be set, depending on the provisioner. | +| `lorawan_version` | [`MACVersion`](#ttn.lorawan.v3.MACVersion) | | LoRaWAN key derivation scheme. | +| `join_nonce` | [`bytes`](#bytes) | | LoRaWAN JoinNonce (or AppNonce). | +| `dev_nonce` | [`bytes`](#bytes) | | LoRaWAN DevNonce. | +| `net_id` | [`bytes`](#bytes) | | LoRaWAN NetID. | +| `provisioner_id` | [`string`](#string) | | Provisioner that provisioned the end device. | +| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Provisioning data for the provisioner. | #### Field Rules @@ -3459,15 +5378,43 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ----------- | | `ids` |

`message.required`: `true`

| | `lorawan_version` |

`enum.defined_only`: `true`

| +| `join_nonce` |

`bytes.len`: `3`

| +| `dev_nonce` |

`bytes.len`: `2`

| +| `net_id` |

`bytes.len`: `3`

| | `provisioner_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +### Message `GetApplicationActivationSettingsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| + +### Message `GetDefaultJoinEUIResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_eui` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + ### Message `GetRootKeysRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | -| `provisioner_id` | [`string`](#string) | | | -| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | End device identifiers to request the root keys for. | +| `provisioner_id` | [`string`](#string) | | Provisioner that provisioned the end device. | +| `provisioning_data` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Provisioning data for the provisioner. | #### Field Rules @@ -3480,9 +5427,9 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `payload_request` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | | | -| `join_request_type` | [`RejoinType`](#ttn.lorawan.v3.RejoinType) | | | -| `dev_nonce` | [`bytes`](#bytes) | | | +| `payload_request` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | | Request data for the cryptographic operation. | +| `join_request_type` | [`JoinRequestType`](#ttn.lorawan.v3.JoinRequestType) | | LoRaWAN join-request type. | +| `dev_nonce` | [`bytes`](#bytes) | | LoRaWAN DevNonce. | #### Field Rules @@ -3490,6 +5437,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ----------- | | `payload_request` |

`message.required`: `true`

| | `join_request_type` |

`enum.defined_only`: `true`

| +| `dev_nonce` |

`bytes.len`: `2`

| ### Message `JoinEUIPrefix` @@ -3498,6 +5446,12 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `join_eui` | [`bytes`](#bytes) | | | | `length` | [`uint32`](#uint32) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + ### Message `JoinEUIPrefixes` | Field | Type | Label | Description | @@ -3535,6 +5489,7 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | Field | Validations | | ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| | `provisioner_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| ### Message `ProvisionEndDevicesRequest.IdentifiersFromData` @@ -3543,6 +5498,12 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | ----- | ---- | ----- | ----------- | | `join_eui` | [`bytes`](#bytes) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + ### Message `ProvisionEndDevicesRequest.IdentifiersList` | Field | Type | Label | Description | @@ -3550,6 +5511,12 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `join_eui` | [`bytes`](#bytes) | | | | `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | repeated | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| + ### Message `ProvisionEndDevicesRequest.IdentifiersRange` | Field | Type | Label | Description | @@ -3557,6 +5524,13 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | `join_eui` | [`bytes`](#bytes) | | | | `start_dev_eui` | [`bytes`](#bytes) | | DevEUI to start issuing from. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| +| `start_dev_eui` |

`bytes.len`: `8`

| + ### Message `SessionKeyRequest` | Field | Type | Label | Description | @@ -3570,6 +5544,49 @@ OrganizationOrUserIdentifiers contains either organization or user identifiers. | Field | Validations | | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| +| `dev_eui` |

`bytes.len`: `8`

| +| `join_eui` |

`bytes.len`: `8`

| + +### Message `SetApplicationActivationSettingsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `settings` | [`ApplicationActivationSettings`](#ttn.lorawan.v3.ApplicationActivationSettings) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `application_ids` |

`message.required`: `true`

| +| `settings` |

`message.required`: `true`

| + +### Service `AppJs` + +The AppJs service connects an Application to a Join Server. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `GetAppSKey` | [`SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) | [`AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) | Request the application session key for a particular session. | + +### Service `ApplicationActivationSettingRegistry` + +The ApplicationActivationSettingRegistry service allows clients to manage their application activation settings. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Get` | [`GetApplicationActivationSettingsRequest`](#ttn.lorawan.v3.GetApplicationActivationSettingsRequest) | [`ApplicationActivationSettings`](#ttn.lorawan.v3.ApplicationActivationSettings) | Get returns application activation settings. | +| `Set` | [`SetApplicationActivationSettingsRequest`](#ttn.lorawan.v3.SetApplicationActivationSettingsRequest) | [`ApplicationActivationSettings`](#ttn.lorawan.v3.ApplicationActivationSettings) | Set creates or updates application activation settings. | +| `Delete` | [`DeleteApplicationActivationSettingsRequest`](#ttn.lorawan.v3.DeleteApplicationActivationSettingsRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete deletes application activation settings. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Get` | `GET` | `/api/v3/js/applications/{application_ids.application_id}/settings` | | +| `Set` | `POST` | `/api/v3/js/applications/{application_ids.application_id}/settings` | `*` | +| `Delete` | `DELETE` | `/api/v3/js/applications/{application_ids.application_id}/settings` | | ### Service `ApplicationCryptoService` @@ -3577,7 +5594,7 @@ Service for application layer cryptographic operations. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `DeriveAppSKey` | [`DeriveSessionKeysRequest`](#ttn.lorawan.v3.DeriveSessionKeysRequest) | [`AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) | | +| `DeriveAppSKey` | [`DeriveSessionKeysRequest`](#ttn.lorawan.v3.DeriveSessionKeysRequest) | [`AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) | Derive the application session key (AppSKey). | | `GetAppKey` | [`GetRootKeysRequest`](#ttn.lorawan.v3.GetRootKeysRequest) | [`KeyEnvelope`](#ttn.lorawan.v3.KeyEnvelope) | Get the AppKey. Crypto Servers may return status code FAILED_PRECONDITION when root keys are not exposed. | ### Service `AsJs` @@ -3586,19 +5603,21 @@ The AsJs service connects an Application Server to a Join Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `GetAppSKey` | [`SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) | [`AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) | | +| `GetAppSKey` | [`SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) | [`AppSKeyResponse`](#ttn.lorawan.v3.AppSKeyResponse) | Request the application session key for a particular session. | ### Service `Js` | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `GetJoinEUIPrefixes` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`JoinEUIPrefixes`](#ttn.lorawan.v3.JoinEUIPrefixes) | | +| `GetJoinEUIPrefixes` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`JoinEUIPrefixes`](#ttn.lorawan.v3.JoinEUIPrefixes) | Request the JoinEUI prefixes that are configured for this Join Server. | +| `GetDefaultJoinEUI` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`GetDefaultJoinEUIResponse`](#ttn.lorawan.v3.GetDefaultJoinEUIResponse) | Request the default JoinEUI that is configured for this Join Server. | #### HTTP bindings | Method Name | Method | Pattern | Body | | ----------- | ------ | ------- | ---- | | `GetJoinEUIPrefixes` | `GET` | `/api/v3/js/join_eui_prefixes` | | +| `GetDefaultJoinEUI` | `GET` | `/api/v3/js/default_join_eui` | | ### Service `JsEndDeviceRegistry` @@ -3627,11 +5646,11 @@ Service for network layer cryptographic operations. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `JoinRequestMIC` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | | -| `JoinAcceptMIC` | [`JoinAcceptMICRequest`](#ttn.lorawan.v3.JoinAcceptMICRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | | -| `EncryptJoinAccept` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | | -| `EncryptRejoinAccept` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | | -| `DeriveNwkSKeys` | [`DeriveSessionKeysRequest`](#ttn.lorawan.v3.DeriveSessionKeysRequest) | [`NwkSKeysResponse`](#ttn.lorawan.v3.NwkSKeysResponse) | | +| `JoinRequestMIC` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | Calculate the join-request message MIC. | +| `JoinAcceptMIC` | [`JoinAcceptMICRequest`](#ttn.lorawan.v3.JoinAcceptMICRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | Calculate the join-accept message MIC. | +| `EncryptJoinAccept` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | Encrypt the join-accept payload. | +| `EncryptRejoinAccept` | [`CryptoServicePayloadRequest`](#ttn.lorawan.v3.CryptoServicePayloadRequest) | [`CryptoServicePayloadResponse`](#ttn.lorawan.v3.CryptoServicePayloadResponse) | Encrypt the rejoin-accept payload. | +| `DeriveNwkSKeys` | [`DeriveSessionKeysRequest`](#ttn.lorawan.v3.DeriveSessionKeysRequest) | [`NwkSKeysResponse`](#ttn.lorawan.v3.NwkSKeysResponse) | Derive network session keys (NwkSKey, or FNwkSKey, SNwkSKey and NwkSEncKey) | | `GetNwkKey` | [`GetRootKeysRequest`](#ttn.lorawan.v3.GetRootKeysRequest) | [`KeyEnvelope`](#ttn.lorawan.v3.KeyEnvelope) | Get the NwkKey. Crypto Servers may return status code FAILED_PRECONDITION when root keys are not exposed. | ### Service `NsJs` @@ -3640,8 +5659,8 @@ The NsJs service connects a Network Server to a Join Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `HandleJoin` | [`JoinRequest`](#ttn.lorawan.v3.JoinRequest) | [`JoinResponse`](#ttn.lorawan.v3.JoinResponse) | | -| `GetNwkSKeys` | [`SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) | [`NwkSKeysResponse`](#ttn.lorawan.v3.NwkSKeysResponse) | | +| `HandleJoin` | [`JoinRequest`](#ttn.lorawan.v3.JoinRequest) | [`JoinResponse`](#ttn.lorawan.v3.JoinResponse) | Handle a join-request message. | +| `GetNwkSKeys` | [`SessionKeyRequest`](#ttn.lorawan.v3.SessionKeyRequest) | [`NwkSKeysResponse`](#ttn.lorawan.v3.NwkSKeysResponse) | Request the network session keys for a particular session. | ## File `lorawan-stack/api/keys.proto` @@ -3657,7 +5676,9 @@ The NsJs service connects a Network Server to a Join Server. | Field | Validations | | ----- | ----------- | +| `key` |

`bytes.len`: `16`

| | `kek_label` |

`string.max_len`: `2048`

| +| `encrypted_key` |

`bytes.max_len`: `1024`

| ### Message `RootKeys` @@ -3747,11 +5768,25 @@ Only the components for which the keys were meant, will have the key-encryption- | ----- | ----------- | | `type` |

`enum.defined_only`: `true`

| +### Message `ClassBCGatewayIdentifiers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `antenna_index` | [`uint32`](#uint32) | | | +| `group_index` | [`uint32`](#uint32) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_ids` |

`message.required`: `true`

| + ### Message `DLSettings` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rx1_dr_offset` | [`uint32`](#uint32) | | | +| `rx1_dr_offset` | [`DataRateOffset`](#ttn.lorawan.v3.DataRateOffset) | | | | `rx2_dr` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | | `opt_neg` | [`bool`](#bool) | | OptNeg is set if Network Server implements LoRaWAN 1.1 or greater. | @@ -3759,7 +5794,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | -| `rx1_dr_offset` |

`uint32.lte`: `7`

| +| `rx1_dr_offset` |

`enum.defined_only`: `true`

| | `rx2_dr` |

`enum.defined_only`: `true`

| ### Message `DataRate` @@ -3768,6 +5803,7 @@ Only the components for which the keys were meant, will have the key-encryption- | ----- | ---- | ----- | ----------- | | `lora` | [`LoRaDataRate`](#ttn.lorawan.v3.LoRaDataRate) | | | | `fsk` | [`FSKDataRate`](#ttn.lorawan.v3.FSKDataRate) | | | +| `lrfhss` | [`LRFHSSDataRate`](#ttn.lorawan.v3.LRFHSSDataRate) | | | ### Message `DataRateIndexValue` @@ -3781,6 +5817,30 @@ Only the components for which the keys were meant, will have the key-encryption- | ----- | ----------- | | `value` |

`enum.defined_only`: `true`

| +### Message `DataRateOffsetValue` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `value` | [`DataRateOffset`](#ttn.lorawan.v3.DataRateOffset) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `value` |

`enum.defined_only`: `true`

| + +### Message `DeviceEIRPValue` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `value` | [`DeviceEIRP`](#ttn.lorawan.v3.DeviceEIRP) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `value` |

`enum.defined_only`: `true`

| + ### Message `DownlinkPath` | Field | Type | Label | Description | @@ -3811,6 +5871,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | +| `dev_addr` |

`bytes.len`: `4`

| | `f_ctrl` |

`message.required`: `true`

| | `f_cnt` |

`uint32.lte`: `65535`

| | `f_opts` |

`bytes.max_len`: `15`

| @@ -3821,6 +5882,18 @@ Only the components for which the keys were meant, will have the key-encryption- | ----- | ---- | ----- | ----------- | | `bit_rate` | [`uint32`](#uint32) | | Bit rate (bps). | +### Message `FrequencyValue` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `value` | [`uint64`](#uint64) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `value` |

`uint64.gte`: `100000`

| + ### Message `GatewayAntennaIdentifiers` | Field | Type | Label | Description | @@ -3850,6 +5923,9 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | +| `join_nonce` |

`bytes.len`: `3`

| +| `net_id` |

`bytes.len`: `3`

| +| `dev_addr` |

`bytes.len`: `4`

| | `dl_settings` |

`message.required`: `true`

| | `rx_delay` |

`enum.defined_only`: `true`

| @@ -3861,12 +5937,29 @@ Only the components for which the keys were meant, will have the key-encryption- | `dev_eui` | [`bytes`](#bytes) | | | | `dev_nonce` | [`bytes`](#bytes) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `join_eui` |

`bytes.len`: `8`

| +| `dev_eui` |

`bytes.len`: `8`

| +| `dev_nonce` |

`bytes.len`: `2`

| + +### Message `LRFHSSDataRate` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `modulation_type` | [`uint32`](#uint32) | | | +| `operating_channel_width` | [`uint32`](#uint32) | | Operating Channel Width (Hz). | +| `coding_rate` | [`string`](#string) | | | + ### Message `LoRaDataRate` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `bandwidth` | [`uint32`](#uint32) | | Bandwidth (Hz). | | `spreading_factor` | [`uint32`](#uint32) | | | +| `coding_rate` | [`string`](#string) | | | ### Message `MACCommand` @@ -4044,7 +6137,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rejoin_type` | [`RejoinType`](#ttn.lorawan.v3.RejoinType) | | | +| `rejoin_type` | [`RejoinRequestType`](#ttn.lorawan.v3.RejoinRequestType) | | | | `data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | | `max_retries` | [`uint32`](#uint32) | | | | `period_exponent` | [`RejoinPeriodExponent`](#ttn.lorawan.v3.RejoinPeriodExponent) | | Exponent e that configures the rejoin period = 32 * 2^e + rand(0,32) seconds. | @@ -4239,7 +6332,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `rx2_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | | -| `rx1_data_rate_offset` | [`uint32`](#uint32) | | | +| `rx1_data_rate_offset` | [`DataRateOffset`](#ttn.lorawan.v3.DataRateOffset) | | | | `rx2_frequency` | [`uint64`](#uint64) | | Rx2 frequency (Hz). | #### Field Rules @@ -4247,7 +6340,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | | `rx2_data_rate_index` |

`enum.defined_only`: `true`

| -| `rx1_data_rate_offset` |

`uint32.lte`: `7`

| +| `rx1_data_rate_offset` |

`enum.defined_only`: `true`

| | `rx2_frequency` |

`uint64.gte`: `100000`

| ### Message `MACCommand.RxTimingSetupReq` @@ -4276,6 +6369,12 @@ Only the components for which the keys were meant, will have the key-encryption- | ----- | ----------- | | `max_eirp_index` |

`enum.defined_only`: `true`

| +### Message `MACCommands` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `commands` | [`MACCommand`](#ttn.lorawan.v3.MACCommand) | repeated | | + ### Message `MACPayload` | Field | Type | Label | Description | @@ -4309,6 +6408,8 @@ Only the components for which the keys were meant, will have the key-encryption- ### Message `Message` +Message represents a LoRaWAN message + | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `m_hdr` | [`MHDR`](#ttn.lorawan.v3.MHDR) | | | @@ -4323,7 +6424,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | | `m_hdr` |

`message.required`: `true`

| -| `mic` |

`bytes.len`: `4`

| +| `mic` |

`bytes.min_len`: `0`

`bytes.max_len`: `4`

| ### Message `PingSlotPeriodValue` @@ -4341,7 +6442,7 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `rejoin_type` | [`RejoinType`](#ttn.lorawan.v3.RejoinType) | | | +| `rejoin_type` | [`RejoinRequestType`](#ttn.lorawan.v3.RejoinRequestType) | | | | `net_id` | [`bytes`](#bytes) | | | | `join_eui` | [`bytes`](#bytes) | | | | `dev_eui` | [`bytes`](#bytes) | | | @@ -4352,6 +6453,9 @@ Only the components for which the keys were meant, will have the key-encryption- | Field | Validations | | ----- | ----------- | | `rejoin_type` |

`enum.defined_only`: `true`

| +| `net_id` |

`bytes.len`: `3`

| +| `join_eui` |

`bytes.len`: `8`

| +| `dev_eui` |

`bytes.len`: `8`

| ### Message `RxDelayValue` @@ -4377,9 +6481,9 @@ Otherwise, the Gateway Server attempts to schedule the request and creates the T | `class` | [`Class`](#ttn.lorawan.v3.Class) | | | | `downlink_paths` | [`DownlinkPath`](#ttn.lorawan.v3.DownlinkPath) | repeated | Downlink paths used to select a gateway for downlink. In class A, the downlink paths are required to only contain uplink tokens. In class B and C, the downlink paths may contain uplink tokens and fixed gateways antenna identifiers. | | `rx1_delay` | [`RxDelay`](#ttn.lorawan.v3.RxDelay) | | Rx1 delay (Rx2 delay is Rx1 delay + 1 second). | -| `rx1_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | LoRaWAN data rate index for Rx1. | +| `rx1_data_rate` | [`DataRate`](#ttn.lorawan.v3.DataRate) | | LoRaWAN data rate for Rx1. | | `rx1_frequency` | [`uint64`](#uint64) | | Frequency (Hz) for Rx1. | -| `rx2_data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | LoRaWAN data rate index for Rx2. | +| `rx2_data_rate` | [`DataRate`](#ttn.lorawan.v3.DataRate) | | LoRaWAN data rate for Rx2. | | `rx2_frequency` | [`uint64`](#uint64) | | Frequency (Hz) for Rx2. | | `priority` | [`TxSchedulePriority`](#ttn.lorawan.v3.TxSchedulePriority) | | Priority for scheduling. Requests with a higher priority are allocated more channel time than messages with a lower priority, in duty-cycle limited regions. A priority of HIGH or higher sets the HiPriorityFlag in the DLMetadata Object. | | `absolute_time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Time when the downlink message should be transmitted. This value is only valid for class C downlink; class A downlink uses uplink tokens and class B downlink is scheduled on ping slots. This requires the gateway to have GPS time sychronization. If the absolute time is not set, the first available time will be used that does not conflict or violate regional limitations. | @@ -4391,8 +6495,6 @@ Otherwise, the Gateway Server attempts to schedule the request and creates the T | Field | Validations | | ----- | ----------- | | `rx1_delay` |

`enum.defined_only`: `true`

| -| `rx1_data_rate_index` |

`enum.defined_only`: `true`

| -| `rx2_data_rate_index` |

`enum.defined_only`: `true`

| | `priority` |

`enum.defined_only`: `true`

| | `frequency_plan_id` |

`string.max_len`: `64`

| @@ -4405,20 +6507,18 @@ On downlink, this is a scheduled transmission. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `data_rate` | [`DataRate`](#ttn.lorawan.v3.DataRate) | | Data rate. | -| `data_rate_index` | [`DataRateIndex`](#ttn.lorawan.v3.DataRateIndex) | | LoRaWAN data rate index. | -| `coding_rate` | [`string`](#string) | | LoRa coding rate. | | `frequency` | [`uint64`](#uint64) | | Frequency (Hz). | | `enable_crc` | [`bool`](#bool) | | Send a CRC in the packet; only on uplink; on downlink, CRC should not be enabled. | | `timestamp` | [`uint32`](#uint32) | | Timestamp of the gateway concentrator when the uplink message was received, or when the downlink message should be transmitted (microseconds). On downlink, set timestamp to 0 and time to null to use immediate scheduling. | | `time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Time of the gateway when the uplink message was received, or when the downlink message should be transmitted. For downlink, this requires the gateway to have GPS time synchronization. | | `downlink` | [`TxSettings.Downlink`](#ttn.lorawan.v3.TxSettings.Downlink) | | Transmission settings for downlink. | +| `concentrator_timestamp` | [`int64`](#int64) | | Concentrator timestamp for the downlink as calculated by the Gateway Server scheduler. This value takes into account necessary offsets such as the RTT (Round Trip Time) and TOA (Time Of Arrival). This field is set and used only by the Gateway Server. | #### Field Rules | Field | Validations | | ----- | ----------- | | `data_rate` |

`message.required`: `true`

| -| `data_rate_index` |

`enum.defined_only`: `true`

| ### Message `TxSettings.Downlink` @@ -4438,6 +6538,7 @@ Transmission settings for downlink. | `timestamp` | [`uint32`](#uint32) | | | | `server_time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Absolute time observed by the server when the uplink message has been received. | | `concentrator_time` | [`int64`](#int64) | | Absolute concentrator time as observed by the Gateway Server, accounting for rollovers. | +| `gateway_time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Absolute time observed by the gateway when the uplink has been received. | #### Field Rules @@ -4445,6 +6546,18 @@ Transmission settings for downlink. | ----- | ----------- | | `ids` |

`message.required`: `true`

| +### Message `ZeroableFrequencyValue` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `value` | [`uint64`](#uint64) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `value` |

`uint64.lte`: `0`

`uint64.gte`: `100000`

| + ### Enum `ADRAckDelayExponent` | Name | Number | Description | @@ -4544,6 +6657,19 @@ Transmission settings for downlink. | `DATA_RATE_14` | 14 | | | `DATA_RATE_15` | 15 | | +### Enum `DataRateOffset` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `DATA_RATE_OFFSET_0` | 0 | | +| `DATA_RATE_OFFSET_1` | 1 | | +| `DATA_RATE_OFFSET_2` | 2 | | +| `DATA_RATE_OFFSET_3` | 3 | | +| `DATA_RATE_OFFSET_4` | 4 | | +| `DATA_RATE_OFFSET_5` | 5 | | +| `DATA_RATE_OFFSET_6` | 6 | | +| `DATA_RATE_OFFSET_7` | 7 | | + ### Enum `DeviceEIRP` | Name | Number | Description | @@ -4565,6 +6691,15 @@ Transmission settings for downlink. | `DEVICE_EIRP_33` | 14 | 33 dBm. | | `DEVICE_EIRP_36` | 15 | 36 dBm. | +### Enum `JoinRequestType` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `REJOIN_CONTEXT` | 0 | Resets DevAddr, Session Keys, Frame Counters, Radio Parameters. | +| `REJOIN_SESSION` | 1 | Equivalent to the initial JoinRequest. | +| `REJOIN_KEYS` | 2 | Resets DevAddr, Session Keys, Frame Counters, while keeping the Radio Parameters. | +| `JOIN` | 255 | Normal join-request. | + ### Enum `MACCommandIdentifier` | Name | Number | Description | @@ -4649,12 +6784,23 @@ Transmission settings for downlink. | ---- | ------ | ----------- | | `PHY_UNKNOWN` | 0 | | | `PHY_V1_0` | 1 | | +| `TS001_V1_0` | 1 | | | `PHY_V1_0_1` | 2 | | +| `TS001_V1_0_1` | 2 | | | `PHY_V1_0_2_REV_A` | 3 | | +| `RP001_V1_0_2` | 3 | | | `PHY_V1_0_2_REV_B` | 4 | | +| `RP001_V1_0_2_REV_B` | 4 | | | `PHY_V1_1_REV_A` | 5 | | +| `RP001_V1_1_REV_A` | 5 | | | `PHY_V1_1_REV_B` | 6 | | +| `RP001_V1_1_REV_B` | 6 | | | `PHY_V1_0_3_REV_A` | 7 | | +| `RP001_V1_0_3_REV_A` | 7 | | +| `RP002_V1_0_0` | 8 | | +| `RP002_V1_0_1` | 9 | | +| `RP002_V1_0_2` | 10 | | +| `RP002_V1_0_3` | 11 | | ### Enum `PingSlotPeriod` @@ -4703,6 +6849,14 @@ Transmission settings for downlink. | `REJOIN_PERIOD_6` | 6 | Every 2048 to 2080 seconds. | | `REJOIN_PERIOD_7` | 7 | Every 4096 to 4128 seconds. | +### Enum `RejoinRequestType` + +| Name | Number | Description | +| ---- | ------ | ----------- | +| `CONTEXT` | 0 | Resets DevAddr, Session Keys, Frame Counters, Radio Parameters. | +| `SESSION` | 1 | Equivalent to the initial JoinRequest. | +| `KEYS` | 2 | Resets DevAddr, Session Keys, Frame Counters, while keeping the Radio Parameters. | + ### Enum `RejoinTimeExponent` | Name | Number | Description | @@ -4724,14 +6878,6 @@ Transmission settings for downlink. | `REJOIN_TIME_14` | 14 | Every ~6.4 months. | | `REJOIN_TIME_15` | 15 | Every ~1.1 year. | -### Enum `RejoinType` - -| Name | Number | Description | -| ---- | ------ | ----------- | -| `CONTEXT` | 0 | Resets DevAddr, Session Keys, Frame Counters, Radio Parameters. | -| `SESSION` | 1 | Equivalent to the initial JoinRequest. | -| `KEYS` | 2 | Resets DevAddr, Session Keys, Frame Counters, while keeping the Radio Parameters. | - ### Enum `RxDelay` | Name | Number | Description | @@ -4765,58 +6911,6 @@ Transmission settings for downlink. | `HIGH` | 5 | | | `HIGHEST` | 6 | | -## File `lorawan-stack/api/message_services.proto` - -### Message `ProcessDownlinkMessageRequest` - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | -| `end_device_version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | | -| `message` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | -| `parameter` | [`string`](#string) | | | - -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `ids` |

`message.required`: `true`

| -| `end_device_version_ids` |

`message.required`: `true`

| -| `message` |

`message.required`: `true`

| - -### Message `ProcessUplinkMessageRequest` - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| `ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | -| `end_device_version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | | -| `message` | [`ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) | | | -| `parameter` | [`string`](#string) | | | - -#### Field Rules - -| Field | Validations | -| ----- | ----------- | -| `ids` |

`message.required`: `true`

| -| `end_device_version_ids` |

`message.required`: `true`

| -| `message` |

`message.required`: `true`

| - -### Service `DownlinkMessageProcessor` - -The DownlinkMessageProcessor service processes downlink messages. - -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `Process` | [`ProcessDownlinkMessageRequest`](#ttn.lorawan.v3.ProcessDownlinkMessageRequest) | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | - -### Service `UplinkMessageProcessor` - -The UplinkMessageProcessor service processes uplink messages. - -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `Process` | [`ProcessUplinkMessageRequest`](#ttn.lorawan.v3.ProcessUplinkMessageRequest) | [`ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) | | - ## File `lorawan-stack/api/messages.proto` ### Message `ApplicationDownlink` @@ -4827,7 +6921,8 @@ The UplinkMessageProcessor service processes uplink messages. | `f_port` | [`uint32`](#uint32) | | | | `f_cnt` | [`uint32`](#uint32) | | | | `frm_payload` | [`bytes`](#bytes) | | The frame payload of the downlink message. The payload is encrypted if the skip_payload_crypto field of the EndDevice is true. | -| `decoded_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `decoded_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | The decoded frame payload of the downlink message. When scheduling downlink with a message processor configured for the end device (see formatters) or application (see default_formatters), this fields acts as input for the downlink encoder, and the output is set to frm_payload. When reading downlink (listing the queue, downlink message events, etc), this fields acts as output of the downlink decoder, and the input is frm_payload. | +| `decoded_payload_warnings` | [`string`](#string) | repeated | Warnings generated by the message processor while encoding frm_payload (scheduling downlink) or decoding the frm_payload (reading downlink). | | `confirmed` | [`bool`](#bool) | | | | `class_b_c` | [`ApplicationDownlink.ClassBC`](#ttn.lorawan.v3.ApplicationDownlink.ClassBC) | | Optional gateway and timing information for class B and C. If set, this downlink message will only be transmitted as class B or C downlink. If not set, this downlink message may be transmitted in class A, B and C. | | `priority` | [`TxSchedulePriority`](#ttn.lorawan.v3.TxSchedulePriority) | | Priority for scheduling the downlink message. | @@ -4838,7 +6933,7 @@ The UplinkMessageProcessor service processes uplink messages. | Field | Validations | | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| -| `f_port` |

`uint32.lte`: `255`

`uint32.gte`: `1`

`uint32.not_in`: `[224]`

| +| `f_port` |

`uint32.lte`: `255`

`uint32.not_in`: `[224]`

| | `priority` |

`enum.defined_only`: `true`

| | `correlation_ids` |

`repeated.items.string.max_len`: `100`

| @@ -4846,7 +6941,7 @@ The UplinkMessageProcessor service processes uplink messages. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `gateways` | [`GatewayAntennaIdentifiers`](#ttn.lorawan.v3.GatewayAntennaIdentifiers) | repeated | Possible gateway identifiers and antenna index to use for this downlink message. The Network Server selects one of these gateways for downlink, based on connectivity, signal quality, channel utilization and an available slot. If none of the gateways can be selected, the downlink message fails. If empty, a gateway and antenna is selected automatically from the gateways seen in recent uplinks. | +| `gateways` | [`ClassBCGatewayIdentifiers`](#ttn.lorawan.v3.ClassBCGatewayIdentifiers) | repeated | Possible gateway identifiers, antenna index, and group index to use for this downlink message. The Network Server selects one of these gateways for downlink, based on connectivity, signal quality, channel utilization and an available slot. If none of the gateways can be selected, the downlink message fails. If empty, a gateway and antenna is selected automatically from the gateways seen in recent uplinks. If group index is set, gateways will be grouped by the index for the Network Server to select one gateway per group. | | `absolute_time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Absolute time when the downlink message should be transmitted. This requires the gateway to have GPS time synchronization. If the time is in the past or if there is a scheduling conflict, the downlink message fails. If null, the time is selected based on slot availability. This is recommended in class B mode. | ### Message `ApplicationDownlinkFailed` @@ -4875,12 +6970,13 @@ The UplinkMessageProcessor service processes uplink messages. | ----- | ---- | ----- | ----------- | | `downlinks` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | repeated | | | `last_f_cnt_down` | [`uint32`](#uint32) | | | +| `session_key_id` | [`bytes`](#bytes) | | | #### Field Rules | Field | Validations | | ----- | ----------- | -| `downlinks` |

`repeated.min_items`: `1`

| +| `session_key_id` |

`bytes.max_len`: `2048`

| ### Message `ApplicationJoinAccept` @@ -4897,6 +6993,7 @@ The UplinkMessageProcessor service processes uplink messages. | Field | Validations | | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| +| `received_at` |

`timestamp.required`: `true`

| ### Message `ApplicationLocation` @@ -4911,7 +7008,7 @@ The UplinkMessageProcessor service processes uplink messages. | Field | Validations | | ----- | ----------- | | `location` |

`message.required`: `true`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| ### Message `ApplicationLocation.AttributesEntry` @@ -4929,12 +7026,15 @@ The UplinkMessageProcessor service processes uplink messages. ### Message `ApplicationUp` +Application uplink message. + | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | | `correlation_ids` | [`string`](#string) | repeated | | | `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Server time when the Application Server received the message. | | `uplink_message` | [`ApplicationUplink`](#ttn.lorawan.v3.ApplicationUplink) | | | +| `uplink_normalized` | [`ApplicationUplinkNormalized`](#ttn.lorawan.v3.ApplicationUplinkNormalized) | | | | `join_accept` | [`ApplicationJoinAccept`](#ttn.lorawan.v3.ApplicationJoinAccept) | | | | `downlink_ack` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | | `downlink_nack` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | | | @@ -4958,16 +7058,57 @@ The UplinkMessageProcessor service processes uplink messages. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `session_key_id` | [`bytes`](#bytes) | | Join Server issued identifier for the session keys used by this uplink. | -| `f_port` | [`uint32`](#uint32) | | | -| `f_cnt` | [`uint32`](#uint32) | | | +| `f_port` | [`uint32`](#uint32) | | LoRaWAN FPort of the uplink message. | +| `f_cnt` | [`uint32`](#uint32) | | LoRaWAN FCntUp of the uplink message. | | `frm_payload` | [`bytes`](#bytes) | | The frame payload of the uplink message. The payload is still encrypted if the skip_payload_crypto field of the EndDevice is true, which is indicated by the presence of the app_s_key field. | -| `decoded_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | | +| `decoded_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | The decoded frame payload of the uplink message. This field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters). | +| `decoded_payload_warnings` | [`string`](#string) | repeated | Warnings generated by the message processor while decoding the frm_payload. | +| `normalized_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | repeated | The normalized frame payload of the uplink message. This field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters). If the message processor is a custom script, there is no uplink normalizer script and the decoded output is valid normalized payload, this field contains the decoded payload. | +| `normalized_payload_warnings` | [`string`](#string) | repeated | Warnings generated by the message processor while normalizing the decoded payload. | | `rx_metadata` | [`RxMetadata`](#ttn.lorawan.v3.RxMetadata) | repeated | A list of metadata for each antenna of each gateway that received this message. | -| `settings` | [`TxSettings`](#ttn.lorawan.v3.TxSettings) | | Settings for the transmission. | +| `settings` | [`TxSettings`](#ttn.lorawan.v3.TxSettings) | | Transmission settings used by the end device. | | `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Server time when the Network Server received the message. | | `app_s_key` | [`KeyEnvelope`](#ttn.lorawan.v3.KeyEnvelope) | | The AppSKey of the current session. This field is only present if the skip_payload_crypto field of the EndDevice is true. Can be used to decrypt uplink payloads and encrypt downlink payloads. | | `last_a_f_cnt_down` | [`uint32`](#uint32) | | The last AFCntDown of the current session. This field is only present if the skip_payload_crypto field of the EndDevice is true. Can be used with app_s_key to encrypt downlink payloads. | -| `confirmed` | [`bool`](#bool) | | next: 12 | +| `confirmed` | [`bool`](#bool) | | Indicates whether the end device used confirmed data uplink. | +| `consumed_airtime` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings. | +| `locations` | [`ApplicationUplink.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplink.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `session_key_id` |

`bytes.max_len`: `2048`

| +| `f_port` |

`uint32.lte`: `255`

`uint32.not_in`: `[224]`

| +| `settings` |

`message.required`: `true`

| + +### Message `ApplicationUplink.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | + +### Message `ApplicationUplinkNormalized` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `session_key_id` | [`bytes`](#bytes) | | Join Server issued identifier for the session keys used by this uplink. | +| `f_port` | [`uint32`](#uint32) | | LoRaWAN FPort of the uplink message. | +| `f_cnt` | [`uint32`](#uint32) | | LoRaWAN FCntUp of the uplink message. | +| `frm_payload` | [`bytes`](#bytes) | | The frame payload of the uplink message. This field is always decrypted with AppSKey. | +| `normalized_payload` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | The normalized frame payload of the uplink message. This field is set for each item in normalized_payload in the corresponding ApplicationUplink message. | +| `normalized_payload_warnings` | [`string`](#string) | repeated | This field is set to normalized_payload_warnings in the corresponding ApplicationUplink message. | +| `rx_metadata` | [`RxMetadata`](#ttn.lorawan.v3.RxMetadata) | repeated | A list of metadata for each antenna of each gateway that received this message. | +| `settings` | [`TxSettings`](#ttn.lorawan.v3.TxSettings) | | Transmission settings used by the end device. | +| `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Server time when the Network Server received the message. | +| `confirmed` | [`bool`](#bool) | | Indicates whether the end device used confirmed data uplink. | +| `consumed_airtime` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings. | +| `locations` | [`ApplicationUplinkNormalized.LocationsEntry`](#ttn.lorawan.v3.ApplicationUplinkNormalized.LocationsEntry) | repeated | End device location metadata, set by the Application Server while handling the message. | +| `version_ids` | [`EndDeviceVersionIdentifiers`](#ttn.lorawan.v3.EndDeviceVersionIdentifiers) | | End device version identifiers, set by the Application Server while handling the message. | +| `network_ids` | [`NetworkIdentifiers`](#ttn.lorawan.v3.NetworkIdentifiers) | | Network identifiers, set by the Network Server that handles the message. | #### Field Rules @@ -4975,8 +7116,16 @@ The UplinkMessageProcessor service processes uplink messages. | ----- | ----------- | | `session_key_id` |

`bytes.max_len`: `2048`

| | `f_port` |

`uint32.lte`: `255`

`uint32.gte`: `1`

`uint32.not_in`: `[224]`

| -| `rx_metadata` |

`repeated.min_items`: `1`

| +| `normalized_payload` |

`message.required`: `true`

| | `settings` |

`message.required`: `true`

| +| `received_at` |

`timestamp.required`: `true`

| + +### Message `ApplicationUplinkNormalized.LocationsEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`Location`](#ttn.lorawan.v3.Location) | | | ### Message `DownlinkMessage` @@ -4990,12 +7139,34 @@ Downlink message from the network to the end device | `request` | [`TxRequest`](#ttn.lorawan.v3.TxRequest) | | | | `scheduled` | [`TxSettings`](#ttn.lorawan.v3.TxSettings) | | | | `correlation_ids` | [`string`](#string) | repeated | | +| `session_key_id` | [`bytes`](#bytes) | | | #### Field Rules | Field | Validations | | ----- | ----------- | | `correlation_ids` |

`repeated.items.string.max_len`: `100`

| +| `session_key_id` |

`bytes.max_len`: `2048`

| + +### Message `DownlinkQueueOperationErrorDetails` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dev_addr` | [`bytes`](#bytes) | | | +| `session_key_id` | [`bytes`](#bytes) | | | +| `min_f_cnt_down` | [`uint32`](#uint32) | | | +| `pending_dev_addr` | [`bytes`](#bytes) | | | +| `pending_session_key_id` | [`bytes`](#bytes) | | | +| `pending_min_f_cnt_down` | [`uint32`](#uint32) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `dev_addr` |

`bytes.len`: `4`

| +| `session_key_id` |

`bytes.max_len`: `2048`

| +| `pending_dev_addr` |

`bytes.len`: `4`

| +| `pending_session_key_id` |

`bytes.max_len`: `2048`

| ### Message `DownlinkQueueRequest` @@ -5004,6 +7175,20 @@ Downlink message from the network to the end device | `end_device_ids` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | | | | `downlinks` | [`ApplicationDownlink`](#ttn.lorawan.v3.ApplicationDownlink) | repeated | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `end_device_ids` |

`message.required`: `true`

| +| `downlinks` |

`repeated.max_items`: `100000`

| + +### Message `GatewayTxAcknowledgment` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `tx_ack` | [`TxAcknowledgment`](#ttn.lorawan.v3.TxAcknowledgment) | | | + ### Message `GatewayUplinkMessage` | Field | Type | Label | Description | @@ -5022,23 +7207,26 @@ Downlink message from the network to the end device | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `up_formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | Payload formatter for uplink messages, must be set together with its parameter. | -| `up_formatter_parameter` | [`string`](#string) | | Parameter for the up_formatter, must be set together. | +| `up_formatter_parameter` | [`string`](#string) | | Parameter for the up_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration. | | `down_formatter` | [`PayloadFormatter`](#ttn.lorawan.v3.PayloadFormatter) | | Payload formatter for downlink messages, must be set together with its parameter. | -| `down_formatter_parameter` | [`string`](#string) | | Parameter for the down_formatter, must be set together. | +| `down_formatter_parameter` | [`string`](#string) | | Parameter for the down_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration. | #### Field Rules | Field | Validations | | ----- | ----------- | | `up_formatter` |

`enum.defined_only`: `true`

| +| `up_formatter_parameter` |

`string.max_len`: `40960`

| | `down_formatter` |

`enum.defined_only`: `true`

| +| `down_formatter_parameter` |

`string.max_len`: `40960`

| ### Message `TxAcknowledgment` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `correlation_ids` | [`string`](#string) | repeated | | +| `correlation_ids` | [`string`](#string) | repeated | Correlation IDs for the downlink message. Set automatically by the UDP and LBS frontends. For gRPC and the MQTT v3 frontends, the correlation IDs must match the ones of the downlink message the Tx acknowledgment message refers to. | | `result` | [`TxAcknowledgment.Result`](#ttn.lorawan.v3.TxAcknowledgment.Result) | | | +| `downlink_message` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | | The acknowledged downlink message. Set by the Gateway Server. | #### Field Rules @@ -5060,6 +7248,7 @@ Uplink message from the end device to the network | `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Server time when a component received the message. The Gateway Server and Network Server set this value to their local server time of reception. | | `correlation_ids` | [`string`](#string) | repeated | | | `device_channel_index` | [`uint32`](#uint32) | | Index of the device channel that received the message. Set by Network Server. | +| `consumed_airtime` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the RawPayload size and the transmission settings. | #### Field Rules @@ -5122,11 +7311,22 @@ More payload formatters can be added. | | `message_id` | [`string`](#string) | | Message identifier generated by Packet Broker Router. | | `forwarder_net_id` | [`bytes`](#bytes) | | LoRa Alliance NetID of the Packet Broker Forwarder Member. | | `forwarder_tenant_id` | [`string`](#string) | | Tenant ID managed by the Packet Broker Forwarder Member. | -| `forwarder_id` | [`string`](#string) | | Forwarder identifier issued by the Packet Broker Forwarder Member. | +| `forwarder_cluster_id` | [`string`](#string) | | Forwarder Cluster ID of the Packet Broker Forwarder. | +| `forwarder_gateway_eui` | [`bytes`](#bytes) | | Forwarder gateway EUI. | +| `forwarder_gateway_id` | [`google.protobuf.StringValue`](#google.protobuf.StringValue) | | Forwarder gateway ID. | | `home_network_net_id` | [`bytes`](#bytes) | | LoRa Alliance NetID of the Packet Broker Home Network Member. | | `home_network_tenant_id` | [`string`](#string) | | Tenant ID managed by the Packet Broker Home Network Member. This value is empty if it cannot be determined by the Packet Broker Router. | +| `home_network_cluster_id` | [`string`](#string) | | Home Network Cluster ID of the Packet Broker Home Network. | | `hops` | [`PacketBrokerRouteHop`](#ttn.lorawan.v3.PacketBrokerRouteHop) | repeated | Hops that the message passed. Each Packet Broker Router service appends an entry. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `forwarder_net_id` |

`bytes.len`: `3`

| +| `forwarder_gateway_eui` |

`bytes.len`: `8`

| +| `home_network_net_id` |

`bytes.len`: `3`

| + ### Message `PacketBrokerRouteHop` | Field | Type | Label | Description | @@ -5147,7 +7347,7 @@ a message corresponds to one RxMetadata. | `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | | `packet_broker` | [`PacketBrokerMetadata`](#ttn.lorawan.v3.PacketBrokerMetadata) | | | | `antenna_index` | [`uint32`](#uint32) | | | -| `time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp at the end of the transmission, provided by the gateway. The accuracy is undefined. | | `timestamp` | [`uint32`](#uint32) | | Gateway concentrator timestamp when the Rx finished (microseconds). | | `fine_timestamp` | [`uint64`](#uint64) | | Gateway's internal fine timestamp when the Rx finished (nanoseconds). | | `encrypted_fine_timestamp` | [`bytes`](#bytes) | | Encrypted gateway's internal fine timestamp when the Rx finished (nanoseconds). | @@ -5162,6 +7362,10 @@ a message corresponds to one RxMetadata. | `downlink_path_constraint` | [`DownlinkPathConstraint`](#ttn.lorawan.v3.DownlinkPathConstraint) | | Gateway downlink path constraint; injected by the Gateway Server. | | `uplink_token` | [`bytes`](#bytes) | | Uplink token to be included in the Tx request in class A downlink; injected by gateway, Gateway Server or fNS. | | `channel_index` | [`uint32`](#uint32) | | Index of the gateway channel that received the message. | +| `hopping_width` | [`uint32`](#uint32) | | Hopping width; a number describing the number of steps of the LR-FHSS grid. | +| `frequency_drift` | [`int32`](#int32) | | Frequency drift in Hz between start and end of an LR-FHSS packet (signed). | +| `gps_time` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp at the end of the transmission, provided by the gateway. Guaranteed to be based on a GPS PPS signal, with an accuracy of 1 millisecond. | +| `received_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp at which the Gateway Server has received the message. | | `advanced` | [`google.protobuf.Struct`](#google.protobuf.Struct) | | Advanced metadata fields - can be used for advanced information or experimental features that are not yet formally defined in the API - field names are written in snake_case | #### Field Rules @@ -5211,59 +7415,245 @@ The connection information of an MQTT frontend. ### Message `GenerateDevAddrResponse` +Response of GenerateDevAddr. + | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `dev_addr` | [`bytes`](#bytes) | | | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `dev_addr` |

`bytes.len`: `4`

| + +### Message `GetDefaultMACSettingsRequest` + +Request of GetDefaultMACSettings. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `frequency_plan_id` | [`string`](#string) | | | +| `lorawan_phy_version` | [`PHYVersion`](#ttn.lorawan.v3.PHYVersion) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `frequency_plan_id` |

`string.max_len`: `64`

| +| `lorawan_phy_version` |

`enum.defined_only`: `true`

| + +### Message `GetDeviceAdressPrefixesResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dev_addr_prefixes` | [`bytes`](#bytes) | repeated | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `dev_addr_prefixes` |

`repeated.items.bytes.len`: `5`

| + +### Message `GetNetIDResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `net_id` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `net_id` |

`bytes.len`: `3`

| + ### Service `AsNs` The AsNs service connects an Application Server to a Network Server. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `LinkApplication` | [`.google.protobuf.Empty`](#google.protobuf.Empty) _stream_ | [`ApplicationUp`](#ttn.lorawan.v3.ApplicationUp) _stream_ | | -| `DownlinkQueueReplace` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `DownlinkQueuePush` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `DownlinkQueueList` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`ApplicationDownlinks`](#ttn.lorawan.v3.ApplicationDownlinks) | | +| `DownlinkQueueReplace` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Replace the entire downlink queue with the specified messages. This can also be used to empty the queue by specifying no messages. Note that this will trigger an immediate downlink if a downlink slot is available. | +| `DownlinkQueuePush` | [`DownlinkQueueRequest`](#ttn.lorawan.v3.DownlinkQueueRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Push downlink messages to the end of the downlink queue. Note that this will trigger an immediate downlink if a downlink slot is available. | +| `DownlinkQueueList` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`ApplicationDownlinks`](#ttn.lorawan.v3.ApplicationDownlinks) | List the items currently in the downlink queue. | ### Service `GsNs` The GsNs service connects a Gateway Server to a Network Server. -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `HandleUplink` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `HandleUplink` | [`UplinkMessage`](#ttn.lorawan.v3.UplinkMessage) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Called by the Gateway Server when an uplink message arrives. | +| `ReportTxAcknowledgment` | [`GatewayTxAcknowledgment`](#ttn.lorawan.v3.GatewayTxAcknowledgment) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Called by the Gateway Server when a Tx acknowledgment arrives. | + +### Service `Ns` + +The Ns service manages the Network Server. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `GenerateDevAddr` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`GenerateDevAddrResponse`](#ttn.lorawan.v3.GenerateDevAddrResponse) | GenerateDevAddr requests a device address assignment from the Network Server. | +| `GetDefaultMACSettings` | [`GetDefaultMACSettingsRequest`](#ttn.lorawan.v3.GetDefaultMACSettingsRequest) | [`MACSettings`](#ttn.lorawan.v3.MACSettings) | GetDefaultMACSettings retrieves the default MAC settings for a frequency plan. | +| `GetNetID` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`GetNetIDResponse`](#ttn.lorawan.v3.GetNetIDResponse) | | +| `GetDeviceAddressPrefixes` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`GetDeviceAdressPrefixesResponse`](#ttn.lorawan.v3.GetDeviceAdressPrefixesResponse) | | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `GenerateDevAddr` | `GET` | `/api/v3/ns/dev_addr` | | +| `GetDefaultMACSettings` | `GET` | `/api/v3/ns/default_mac_settings/{frequency_plan_id}/{lorawan_phy_version}` | | +| `GetNetID` | `GET` | `/api/v3/ns/net_id` | | +| `GetDeviceAddressPrefixes` | `GET` | `/api/v3/ns/dev_addr_prefixes` | | + +### Service `NsEndDeviceRegistry` + +The NsEndDeviceRegistry service allows clients to manage their end devices on the Network Server. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Get` | [`GetEndDeviceRequest`](#ttn.lorawan.v3.GetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Get returns the device that matches the given identifiers. If there are multiple matches, an error will be returned. | +| `Set` | [`SetEndDeviceRequest`](#ttn.lorawan.v3.SetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Set creates or updates the device. | +| `ResetFactoryDefaults` | [`ResetAndGetEndDeviceRequest`](#ttn.lorawan.v3.ResetAndGetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | ResetFactoryDefaults resets device state to factory defaults. | +| `Delete` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete deletes the device that matches the given identifiers. If there are multiple matches, an error will be returned. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Get` | `GET` | `/api/v3/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}` | | +| `Set` | `PUT` | `/api/v3/ns/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}` | `*` | +| `Set` | `POST` | `/api/v3/ns/applications/{end_device.ids.application_ids.application_id}/devices` | `*` | +| `ResetFactoryDefaults` | `PATCH` | `/api/v3/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}` | `*` | +| `Delete` | `DELETE` | `/api/v3/ns/applications/{application_ids.application_id}/devices/{device_id}` | | + +## File `lorawan-stack/api/notification_service.proto` + +### Message `CreateNotificationRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `entity_ids` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | | The entity this notification is about. | +| `notification_type` | [`string`](#string) | | The type of this notification. | +| `data` | [`google.protobuf.Any`](#google.protobuf.Any) | | The data related to the notification. | +| `sender_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification. | +| `receivers` | [`NotificationReceiver`](#ttn.lorawan.v3.NotificationReceiver) | repeated | Receivers of the notification. | +| `email` | [`bool`](#bool) | | Whether an email should be sent for the notification. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `entity_ids` |

`message.required`: `true`

| +| `notification_type` |

`string.min_len`: `1`

`string.max_len`: `100`

| +| `receivers` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| + +### Message `CreateNotificationResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [`string`](#string) | | | + +### Message `EntityStateChangedNotification` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `state` | [`State`](#ttn.lorawan.v3.State) | | | +| `state_description` | [`string`](#string) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `state` |

`enum.defined_only`: `true`

| +| `state_description` |

`string.max_len`: `128`

| + +### Message `ListNotificationsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `receiver_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | The IDs of the receiving user. | +| `status` | [`NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) | repeated | Select notifications with these statuses. An empty list is interpreted as "all". | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `receiver_ids` |

`message.required`: `true`

| +| `status` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `ListNotificationsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `notifications` | [`Notification`](#ttn.lorawan.v3.Notification) | repeated | | + +### Message `Notification` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [`string`](#string) | | The immutable ID of the notification. Generated by the server. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | The time when the notification was triggered. | +| `entity_ids` | [`EntityIdentifiers`](#ttn.lorawan.v3.EntityIdentifiers) | | The entity this notification is about. | +| `notification_type` | [`string`](#string) | | The type of this notification. | +| `data` | [`google.protobuf.Any`](#google.protobuf.Any) | | The data related to the notification. | +| `sender_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification. | +| `receivers` | [`NotificationReceiver`](#ttn.lorawan.v3.NotificationReceiver) | repeated | Relation of the notification receiver to the entity. | +| `email` | [`bool`](#bool) | | Whether an email was sent for the notification. | +| `status` | [`NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) | | The status of the notification. | +| `status_updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | The time when the notification status was updated. | + +### Message `UpdateNotificationStatusRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `receiver_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | The IDs of the receiving user. | +| `ids` | [`string`](#string) | repeated | The IDs of the notifications to update the status of. | +| `status` | [`NotificationStatus`](#ttn.lorawan.v3.NotificationStatus) | | The status to set on the notifications. | + +#### Field Rules -### Service `Ns` +| Field | Validations | +| ----- | ----------- | +| `receiver_ids` |

`message.required`: `true`

| +| `ids` |

`repeated.min_items`: `1`

`repeated.max_items`: `1000`

`repeated.unique`: `true`

`repeated.items.string.len`: `36`

| +| `status` |

`enum.defined_only`: `true`

| -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `GenerateDevAddr` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`GenerateDevAddrResponse`](#ttn.lorawan.v3.GenerateDevAddrResponse) | GenerateDevAddr requests a device address assignment from the Network Server. | +### Enum `NotificationReceiver` -#### HTTP bindings +| Name | Number | Description | +| ---- | ------ | ----------- | +| `NOTIFICATION_RECEIVER_UNKNOWN` | 0 | | +| `NOTIFICATION_RECEIVER_COLLABORATOR` | 1 | Notification is received by collaborators of the entity. If the collaborator is an organization, the notification is received by organization members. | +| `NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT` | 3 | Notification is received by administrative contact of the entity. If this is an organization, the notification is received by organization members. | +| `NOTIFICATION_RECEIVER_TECHNICAL_CONTACT` | 4 | Notification is received by technical contact of the entity. If this is an organization, the notification is received by organization members. | -| Method Name | Method | Pattern | Body | -| ----------- | ------ | ------- | ---- | -| `GenerateDevAddr` | `GET` | `/api/v3/ns/dev_addr` | | +### Enum `NotificationStatus` -### Service `NsEndDeviceRegistry` +| Name | Number | Description | +| ---- | ------ | ----------- | +| `NOTIFICATION_STATUS_UNSEEN` | 0 | | +| `NOTIFICATION_STATUS_SEEN` | 1 | | +| `NOTIFICATION_STATUS_ARCHIVED` | 2 | | -The NsEndDeviceRegistry service allows clients to manage their end devices on the Network Server. +### Service `NotificationService` | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `Get` | [`GetEndDeviceRequest`](#ttn.lorawan.v3.GetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Get returns the device that matches the given identifiers. If there are multiple matches, an error will be returned. | -| `Set` | [`SetEndDeviceRequest`](#ttn.lorawan.v3.SetEndDeviceRequest) | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | Set creates or updates the device. | -| `Delete` | [`EndDeviceIdentifiers`](#ttn.lorawan.v3.EndDeviceIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete deletes the device that matches the given identifiers. If there are multiple matches, an error will be returned. | +| `Create` | [`CreateNotificationRequest`](#ttn.lorawan.v3.CreateNotificationRequest) | [`CreateNotificationResponse`](#ttn.lorawan.v3.CreateNotificationResponse) | Create a new notification. Can only be called by internal services using cluster auth. | +| `List` | [`ListNotificationsRequest`](#ttn.lorawan.v3.ListNotificationsRequest) | [`ListNotificationsResponse`](#ttn.lorawan.v3.ListNotificationsResponse) | List the notifications for a user or an organization. When called with user credentials and empty receiver_ids, this will list notifications for the current user and its organizations. | +| `UpdateStatus` | [`UpdateNotificationStatusRequest`](#ttn.lorawan.v3.UpdateNotificationStatusRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Batch-update multiple notifications to the same status. | #### HTTP bindings | Method Name | Method | Pattern | Body | | ----------- | ------ | ------- | ---- | -| `Get` | `GET` | `/api/v3/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}` | | -| `Set` | `PUT` | `/api/v3/ns/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}` | `*` | -| `Set` | `POST` | `/api/v3/ns/applications/{end_device.ids.application_ids.application_id}/devices` | `*` | -| `Delete` | `DELETE` | `/api/v3/ns/applications/{application_ids.application_id}/devices/{device_id}` | | +| `List` | `` | `/api/v3` | | +| `List` | `GET` | `/api/v3/users/{receiver_ids.user_id}/notifications` | | +| `UpdateStatus` | `PATCH` | `/api/v3/users/{receiver_ids.user_id}/notifications` | `*` | ## File `lorawan-stack/api/oauth.proto` @@ -5410,12 +7800,15 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th ### Service `OAuthAuthorizationRegistry` +The OAuthAuthorizationRegistry service, exposed by the Identity Server, +is used to manage OAuth client authorizations for users. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `List` | [`ListOAuthClientAuthorizationsRequest`](#ttn.lorawan.v3.ListOAuthClientAuthorizationsRequest) | [`OAuthClientAuthorizations`](#ttn.lorawan.v3.OAuthClientAuthorizations) | | -| `ListTokens` | [`ListOAuthAccessTokensRequest`](#ttn.lorawan.v3.ListOAuthAccessTokensRequest) | [`OAuthAccessTokens`](#ttn.lorawan.v3.OAuthAccessTokens) | | -| `Delete` | [`OAuthClientAuthorizationIdentifiers`](#ttn.lorawan.v3.OAuthClientAuthorizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | -| `DeleteToken` | [`OAuthAccessTokenIdentifiers`](#ttn.lorawan.v3.OAuthAccessTokenIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| `List` | [`ListOAuthClientAuthorizationsRequest`](#ttn.lorawan.v3.ListOAuthClientAuthorizationsRequest) | [`OAuthClientAuthorizations`](#ttn.lorawan.v3.OAuthClientAuthorizations) | List OAuth clients that are authorized by the user. | +| `ListTokens` | [`ListOAuthAccessTokensRequest`](#ttn.lorawan.v3.ListOAuthAccessTokensRequest) | [`OAuthAccessTokens`](#ttn.lorawan.v3.OAuthAccessTokens) | List OAuth access tokens issued to the OAuth client on behalf of the user. | +| `Delete` | [`OAuthClientAuthorizationIdentifiers`](#ttn.lorawan.v3.OAuthClientAuthorizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete (de-authorize) an OAuth client for the user. | +| `DeleteToken` | [`OAuthAccessTokenIdentifiers`](#ttn.lorawan.v3.OAuthAccessTokenIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete (invalidate) an OAuth access token. | #### HTTP bindings @@ -5435,6 +7828,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | | `name` | [`string`](#string) | | | | `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | #### Field Rules @@ -5442,7 +7836,8 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | ----- | ----------- | | `organization_ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| -| `rights` |

`repeated.items.enum.defined_only`: `true`

| +| `rights` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `expires_at` |

`timestamp.gt_now`: `true`

| ### Message `CreateOrganizationRequest` @@ -5503,6 +7898,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | +| `order` | [`string`](#string) | | Order the results by this field path. Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | @@ -5511,6 +7907,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | Field | Validations | | ----- | ----------- | | `organization_ids` |

`message.required`: `true`

| +| `order` |

`string.in`: `[ api_key_id -api_key_id name -name created_at -created_at expires_at -expires_at]`

| | `limit` |

`uint32.lte`: `1000`

| ### Message `ListOrganizationCollaboratorsRequest` @@ -5520,6 +7917,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | #### Field Rules @@ -5527,6 +7925,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | ----- | ----------- | | `organization_ids` |

`message.required`: `true`

| | `limit` |

`uint32.lte`: `1000`

| +| `order` |

`string.in`: `[ id -id -rights rights]`

| ### Message `ListOrganizationsRequest` @@ -5537,6 +7936,7 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted organizations. | #### Field Rules @@ -5549,13 +7949,16 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `name` | [`string`](#string) | | | -| `description` | [`string`](#string) | | | +| `ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | The identifiers of the organization. These are public and can be seen by any authenticated user in the network. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the organization was created. This information is public and can be seen by any authenticated user in the network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the organization was last updated. This information is public and can be seen by any authenticated user in the network. | +| `deleted_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the organization was deleted. This information is public and can be seen by any authenticated user in the network. | +| `name` | [`string`](#string) | | The name of the organization. This information is public and can be seen by any authenticated user in the network. | +| `description` | [`string`](#string) | | A description for the organization. | | `attributes` | [`Organization.AttributesEntry`](#ttn.lorawan.v3.Organization.AttributesEntry) | repeated | Key-value attributes for this organization. Typically used for organizing organizations or for storing integration-specific data. | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization. This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `technical_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | #### Field Rules @@ -5564,7 +7967,8 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| +| `contact_info` |

`repeated.max_items`: `10`

| ### Message `Organization.AttributesEntry` @@ -5590,87 +7994,385 @@ The NsEndDeviceRegistry service allows clients to manage their end devices on th | Field | Validations | | ----- | ----------- | -| `organization_ids` |

`message.required`: `true`

| -| `collaborator` |

`message.required`: `true`

| +| `organization_ids` |

`message.required`: `true`

| +| `collaborator` |

`message.required`: `true`

| + +### Message `UpdateOrganizationAPIKeyRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | +| `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the api key fields that should be updated. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `organization_ids` |

`message.required`: `true`

| +| `api_key` |

`message.required`: `true`

| + +### Message `UpdateOrganizationRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `organization` | [`Organization`](#ttn.lorawan.v3.Organization) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the organization fields that should be updated. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `organization` |

`message.required`: `true`

| + +## File `lorawan-stack/api/organization_services.proto` + +### Service `OrganizationAccess` + +The OrganizationAcces service, exposed by the Identity Server, is used to manage +API keys and collaborators of organizations. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `ListRights` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this organization. | +| `CreateAPIKey` | [`CreateOrganizationAPIKeyRequest`](#ttn.lorawan.v3.CreateOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Create an API key scoped to this organization. Organization API keys can give access to the organization itself, as well as any application, gateway and OAuth client this organization is a collaborator of. | +| `ListAPIKeys` | [`ListOrganizationAPIKeysRequest`](#ttn.lorawan.v3.ListOrganizationAPIKeysRequest) | [`APIKeys`](#ttn.lorawan.v3.APIKeys) | List the API keys for this organization. | +| `GetAPIKey` | [`GetOrganizationAPIKeyRequest`](#ttn.lorawan.v3.GetOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Get a single API key of this organization. | +| `UpdateAPIKey` | [`UpdateOrganizationAPIKeyRequest`](#ttn.lorawan.v3.UpdateOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Update the rights of an API key of the organization. This method can also be used to delete the API key, by giving it no rights. The caller is required to have all assigned or/and removed rights. | +| `GetCollaborator` | [`GetOrganizationCollaboratorRequest`](#ttn.lorawan.v3.GetOrganizationCollaboratorRequest) | [`GetCollaboratorResponse`](#ttn.lorawan.v3.GetCollaboratorResponse) | Get the rights of a collaborator (member) of the organization. Pseudo-rights in the response (such as the "_ALL" right) are not expanded. | +| `SetCollaborator` | [`SetOrganizationCollaboratorRequest`](#ttn.lorawan.v3.SetOrganizationCollaboratorRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Set the rights of a collaborator (member) on the organization. Organization collaborators can get access to the organization itself, as well as any application, gateway and OAuth client this organization is a collaborator of. This method can also be used to delete the collaborator, by giving them no rights. The caller is required to have all assigned or/and removed rights. | +| `ListCollaborators` | [`ListOrganizationCollaboratorsRequest`](#ttn.lorawan.v3.ListOrganizationCollaboratorsRequest) | [`Collaborators`](#ttn.lorawan.v3.Collaborators) | List the collaborators on this organization. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `ListRights` | `GET` | `/api/v3/organizations/{organization_id}/rights` | | +| `CreateAPIKey` | `POST` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys` | `*` | +| `ListAPIKeys` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys` | | +| `GetAPIKey` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys/{key_id}` | | +| `UpdateAPIKey` | `PUT` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys/{api_key.id}` | `*` | +| `GetCollaborator` | `` | `/api/v3` | | +| `GetCollaborator` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/collaborator/user/{collaborator.user_ids.user_id}` | | +| `SetCollaborator` | `PUT` | `/api/v3/organizations/{organization_ids.organization_id}/collaborators` | `*` | +| `ListCollaborators` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/collaborators` | | + +### Service `OrganizationRegistry` + +The OrganizationRegistry service, exposed by the Identity Server, is used to manage +organization registrations. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `Create` | [`CreateOrganizationRequest`](#ttn.lorawan.v3.CreateOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Create a new organization. This also sets the given user as first collaborator with all possible rights. | +| `Get` | [`GetOrganizationRequest`](#ttn.lorawan.v3.GetOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Get the organization with the given identifiers, selecting the fields specified in the field mask. More or less fields may be returned, depending on the rights of the caller. | +| `List` | [`ListOrganizationsRequest`](#ttn.lorawan.v3.ListOrganizationsRequest) | [`Organizations`](#ttn.lorawan.v3.Organizations) | List organizations where the given user or organization is a direct collaborator. If no user or organization is given, this returns the organizations the caller has access to. Similar to Get, this selects the fields given by the field mask. More or less fields may be returned, depending on the rights of the caller. | +| `Update` | [`UpdateOrganizationRequest`](#ttn.lorawan.v3.UpdateOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Update the organization, changing the fields specified by the field mask to the provided values. | +| `Delete` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the organization. This may not release the organization ID for reuse. | +| `Restore` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Restore a recently deleted organization. + +Deployment configuration may specify if, and for how long after deletion, entities can be restored. | +| `Purge` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Purge the organization. This will release the organization ID for reuse. The user is responsible for clearing data from any (external) integrations that may store and expose data by user or organization ID. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `Create` | `POST` | `/api/v3/users/{collaborator.user_ids.user_id}/organizations` | `*` | +| `Get` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}` | | +| `List` | `GET` | `/api/v3/organizations` | | +| `List` | `GET` | `/api/v3/users/{collaborator.user_ids.user_id}/organizations` | | +| `Update` | `PUT` | `/api/v3/organizations/{organization.ids.organization_id}` | `*` | +| `Delete` | `DELETE` | `/api/v3/organizations/{organization_id}` | | +| `Restore` | `POST` | `/api/v3/organizations/{organization_id}/restore` | | +| `Purge` | `DELETE` | `/api/v3/organizations/{organization_id}/purge` | | + +## File `lorawan-stack/api/packetbrokeragent.proto` + +### Message `ListForwarderRoutingPoliciesRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `home_network_id` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | | Packet Broker identifier of the Home Network. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | + +### Message `ListHomeNetworkRoutingPoliciesRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `limit` |

`uint32.lte`: `1000`

| + +### Message `ListPacketBrokerHomeNetworksRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `tenant_id_contains` | [`string`](#string) | | Filter by tenant ID. | +| `name_contains` | [`string`](#string) | | Filter by name. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `limit` |

`uint32.lte`: `1000`

| +| `tenant_id_contains` |

`string.max_len`: `100`

| +| `name_contains` |

`string.max_len`: `100`

| + +### Message `ListPacketBrokerNetworksRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `with_routing_policy` | [`bool`](#bool) | | If true, list only the Forwarders and Home Networks with whom a routing policy has been defined in either direction. | +| `tenant_id_contains` | [`string`](#string) | | Filter by tenant ID. | +| `name_contains` | [`string`](#string) | | Filter by name. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `limit` |

`uint32.lte`: `1000`

| +| `tenant_id_contains` |

`string.max_len`: `100`

| +| `name_contains` |

`string.max_len`: `100`

| + +### Message `PacketBrokerDefaultGatewayVisibility` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp when the policy got last updated. | +| `visibility` | [`PacketBrokerGatewayVisibility`](#ttn.lorawan.v3.PacketBrokerGatewayVisibility) | | | + +### Message `PacketBrokerDefaultRoutingPolicy` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp when the policy got last updated. | +| `uplink` | [`PacketBrokerRoutingPolicyUplink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyUplink) | | Uplink policy. | +| `downlink` | [`PacketBrokerRoutingPolicyDownlink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyDownlink) | | Downlink policy. | + +### Message `PacketBrokerDevAddrBlock` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `dev_addr_prefix` | [`DevAddrPrefix`](#ttn.lorawan.v3.DevAddrPrefix) | | | +| `home_network_cluster_id` | [`string`](#string) | | | + +### Message `PacketBrokerGateway` + +Gateway respresentation for Packet Broker. +This is a subset and superset of the Gateway message using the same data types and field tags to achieve initial wire compatibility. +There is no (longer) wire compatibility needed; new fields may use any tag. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `ids` | [`PacketBrokerGateway.GatewayIdentifiers`](#ttn.lorawan.v3.PacketBrokerGateway.GatewayIdentifiers) | | | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `technical_contact` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | | | +| `antennas` | [`GatewayAntenna`](#ttn.lorawan.v3.GatewayAntenna) | repeated | | +| `status_public` | [`bool`](#bool) | | | +| `location_public` | [`bool`](#bool) | | | +| `frequency_plan_ids` | [`string`](#string) | repeated | | +| `update_location_from_status` | [`bool`](#bool) | | | +| `online` | [`bool`](#bool) | | | +| `rx_rate` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Received packets rate (number of packets per hour). This field gets updated when a value is set. | +| `tx_rate` | [`google.protobuf.FloatValue`](#google.protobuf.FloatValue) | | Transmitted packets rate (number of packets per hour). This field gets updated when a value is set. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `ids` |

`message.required`: `true`

| +| `contact_info` |

`repeated.max_items`: `10`

| +| `antennas` |

`repeated.max_items`: `8`

| +| `frequency_plan_ids` |

`repeated.max_items`: `8`

`repeated.items.string.max_len`: `64`

| + +### Message `PacketBrokerGateway.GatewayIdentifiers` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway_id` | [`string`](#string) | | | +| `eui` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `gateway_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[_-]?[a-z0-9]){2,}$`

| +| `eui` |

`bytes.len`: `8`

| + +### Message `PacketBrokerGatewayVisibility` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `location` | [`bool`](#bool) | | Show location. | +| `antenna_placement` | [`bool`](#bool) | | Show antenna placement (indoor/outdoor). | +| `antenna_count` | [`bool`](#bool) | | Show antenna count. | +| `fine_timestamps` | [`bool`](#bool) | | Show whether the gateway produces fine timestamps. | +| `contact_info` | [`bool`](#bool) | | Show contact information. | +| `status` | [`bool`](#bool) | | Show status (online/offline). | +| `frequency_plan` | [`bool`](#bool) | | Show frequency plan. | +| `packet_rates` | [`bool`](#bool) | | Show receive and transmission packet rates. | + +### Message `PacketBrokerInfo` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `registration` | [`PacketBrokerNetwork`](#ttn.lorawan.v3.PacketBrokerNetwork) | | The current registration, unset if there isn't a registration. | +| `forwarder_enabled` | [`bool`](#bool) | | Whether the server is configured as Forwarder (with gateways). | +| `home_network_enabled` | [`bool`](#bool) | | Whether the server is configured as Home Network (with end devices). | +| `register_enabled` | [`bool`](#bool) | | Whether the registration can be changed. | + +### Message `PacketBrokerNetwork` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `id` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | | Packet Broker network identifier. | +| `name` | [`string`](#string) | | Name of the network. | +| `dev_addr_blocks` | [`PacketBrokerDevAddrBlock`](#ttn.lorawan.v3.PacketBrokerDevAddrBlock) | repeated | DevAddr blocks that are assigned to this registration. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information. This field is deprecated. Use administrative_contact and technical_contact instead. | +| `administrative_contact` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | | | +| `technical_contact` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | | | +| `listed` | [`bool`](#bool) | | Whether the network is listed so it can be viewed by other networks. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `contact_info` |

`repeated.max_items`: `10`

| + +### Message `PacketBrokerNetworkIdentifier` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `net_id` | [`uint32`](#uint32) | | LoRa Alliance NetID. | +| `tenant_id` | [`string`](#string) | | Tenant identifier if the registration leases DevAddr blocks from a NetID. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `tenant_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| + +### Message `PacketBrokerNetworks` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `networks` | [`PacketBrokerNetwork`](#ttn.lorawan.v3.PacketBrokerNetwork) | repeated | | + +### Message `PacketBrokerRegisterRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `listed` | [`google.protobuf.BoolValue`](#google.protobuf.BoolValue) | | Whether the network should be listed in Packet Broker. If unset, the value is taken from the registration settings. | + +### Message `PacketBrokerRoutingPolicies` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `policies` | [`PacketBrokerRoutingPolicy`](#ttn.lorawan.v3.PacketBrokerRoutingPolicy) | repeated | | + +### Message `PacketBrokerRoutingPolicy` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `forwarder_id` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | | Packet Broker identifier of the Forwarder. | +| `home_network_id` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | | Packet Broker identifier of the Home Network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | Timestamp when the policy got last updated. | +| `uplink` | [`PacketBrokerRoutingPolicyUplink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyUplink) | | Uplink policy. | +| `downlink` | [`PacketBrokerRoutingPolicyDownlink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyDownlink) | | Downlink policy. | + +### Message `PacketBrokerRoutingPolicyDownlink` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_accept` | [`bool`](#bool) | | Allow join-accept messages. | +| `mac_data` | [`bool`](#bool) | | Allow downlink messages with FPort of 0. | +| `application_data` | [`bool`](#bool) | | Allow downlink messages with FPort between 1 and 255. | + +### Message `PacketBrokerRoutingPolicyUplink` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `join_request` | [`bool`](#bool) | | Forward join-request messages. | +| `mac_data` | [`bool`](#bool) | | Forward uplink messages with FPort of 0. | +| `application_data` | [`bool`](#bool) | | Forward uplink messages with FPort between 1 and 255. | +| `signal_quality` | [`bool`](#bool) | | Forward RSSI and SNR. | +| `localization` | [`bool`](#bool) | | Forward gateway location, RSSI, SNR and fine timestamp. | + +### Message `SetPacketBrokerDefaultGatewayVisibilityRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `visibility` | [`PacketBrokerGatewayVisibility`](#ttn.lorawan.v3.PacketBrokerGatewayVisibility) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `visibility` |

`message.required`: `true`

| -### Message `UpdateOrganizationAPIKeyRequest` +### Message `SetPacketBrokerDefaultRoutingPolicyRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | -| `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `uplink` | [`PacketBrokerRoutingPolicyUplink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyUplink) | | Uplink policy. | +| `downlink` | [`PacketBrokerRoutingPolicyDownlink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyDownlink) | | Downlink policy. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `organization_ids` |

`message.required`: `true`

| -| `api_key` |

`message.required`: `true`

| +| `uplink` |

`message.required`: `true`

| +| `downlink` |

`message.required`: `true`

| -### Message `UpdateOrganizationRequest` +### Message `SetPacketBrokerRoutingPolicyRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `organization` | [`Organization`](#ttn.lorawan.v3.Organization) | | | -| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the organization fields that should be updated. | +| `home_network_id` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | | Packet Broker identifier of the Home Network. | +| `uplink` | [`PacketBrokerRoutingPolicyUplink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyUplink) | | Uplink policy. | +| `downlink` | [`PacketBrokerRoutingPolicyDownlink`](#ttn.lorawan.v3.PacketBrokerRoutingPolicyDownlink) | | Downlink policy. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `organization` |

`message.required`: `true`

| - -## File `lorawan-stack/api/organization_services.proto` - -### Service `OrganizationAccess` +| `uplink` |

`message.required`: `true`

| +| `downlink` |

`message.required`: `true`

| -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `ListRights` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this organization. | -| `CreateAPIKey` | [`CreateOrganizationAPIKeyRequest`](#ttn.lorawan.v3.CreateOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Create an API key scoped to this organization. Organization API keys can give access to the organization itself, as well as any application, gateway and OAuth client this organization is a collaborator of. | -| `ListAPIKeys` | [`ListOrganizationAPIKeysRequest`](#ttn.lorawan.v3.ListOrganizationAPIKeysRequest) | [`APIKeys`](#ttn.lorawan.v3.APIKeys) | List the API keys for this organization. | -| `GetAPIKey` | [`GetOrganizationAPIKeyRequest`](#ttn.lorawan.v3.GetOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Get a single API key of this organization. | -| `UpdateAPIKey` | [`UpdateOrganizationAPIKeyRequest`](#ttn.lorawan.v3.UpdateOrganizationAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Update the rights of an API key of the organization. This method can also be used to delete the API key, by giving it no rights. The caller is required to have all assigned or/and removed rights. | -| `GetCollaborator` | [`GetOrganizationCollaboratorRequest`](#ttn.lorawan.v3.GetOrganizationCollaboratorRequest) | [`GetCollaboratorResponse`](#ttn.lorawan.v3.GetCollaboratorResponse) | Get the rights of a collaborator (member) of the organization. Pseudo-rights in the response (such as the "_ALL" right) are not expanded. | -| `SetCollaborator` | [`SetOrganizationCollaboratorRequest`](#ttn.lorawan.v3.SetOrganizationCollaboratorRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Set the rights of a collaborator (member) on the organization. Organization collaborators can get access to the organization itself, as well as any application, gateway and OAuth client this organization is a collaborator of. This method can also be used to delete the collaborator, by giving them no rights. The caller is required to have all assigned or/and removed rights. | -| `ListCollaborators` | [`ListOrganizationCollaboratorsRequest`](#ttn.lorawan.v3.ListOrganizationCollaboratorsRequest) | [`Collaborators`](#ttn.lorawan.v3.Collaborators) | List the collaborators on this organization. | +### Message `UpdatePacketBrokerGatewayRequest` -#### HTTP bindings +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `gateway` | [`PacketBrokerGateway`](#ttn.lorawan.v3.PacketBrokerGateway) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the gateway fields that are considered for update. -| Method Name | Method | Pattern | Body | -| ----------- | ------ | ------- | ---- | -| `ListRights` | `GET` | `/api/v3/organizations/{organization_id}/rights` | | -| `CreateAPIKey` | `POST` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys` | `*` | -| `ListAPIKeys` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys` | | -| `GetAPIKey` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys/{key_id}` | | -| `UpdateAPIKey` | `PUT` | `/api/v3/organizations/{organization_ids.organization_id}/api-keys/{api_key.id}` | `*` | -| `GetCollaborator` | `` | `/api/v3` | | -| `GetCollaborator` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/collaborator/user/{collaborator.user_ids.user_id}` | | -| `SetCollaborator` | `PUT` | `/api/v3/organizations/{organization_ids.organization_id}/collaborators` | `*` | -| `ListCollaborators` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/collaborators` | | +Online status is only updated if status_public is set. If status_public is set and false, the status will be reset. If status_public is set and true, the online status is taken from the online field. The return message contains the duration online_ttl for how long the gateway is considered online. -### Service `OrganizationRegistry` +Location is only updated if location_public is set. If location_public is set and false, the location will be reset. If location_public is set and true, the first antenna location will be used as gateway location. | -| Method Name | Request Type | Response Type | Description | -| ----------- | ------------ | ------------- | ------------| -| `Create` | [`CreateOrganizationRequest`](#ttn.lorawan.v3.CreateOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Create a new organization. This also sets the given user as first collaborator with all possible rights. | -| `Get` | [`GetOrganizationRequest`](#ttn.lorawan.v3.GetOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Get the organization with the given identifiers, selecting the fields specified in the field mask. More or less fields may be returned, depending on the rights of the caller. | -| `List` | [`ListOrganizationsRequest`](#ttn.lorawan.v3.ListOrganizationsRequest) | [`Organizations`](#ttn.lorawan.v3.Organizations) | List organizations where the given user or organization is a direct collaborator. If no user or organization is given, this returns the organizations the caller has access to. Similar to Get, this selects the fields given by the field mask. More or less fields may be returned, depending on the rights of the caller. | -| `Update` | [`UpdateOrganizationRequest`](#ttn.lorawan.v3.UpdateOrganizationRequest) | [`Organization`](#ttn.lorawan.v3.Organization) | Update the organization, changing the fields specified by the field mask to the provided values. | -| `Delete` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the organization. This may not release the organization ID for reuse. | +#### Field Rules -#### HTTP bindings +| Field | Validations | +| ----- | ----------- | +| `gateway` |

`message.required`: `true`

| -| Method Name | Method | Pattern | Body | -| ----------- | ------ | ------- | ---- | -| `Create` | `POST` | `/api/v3/users/{collaborator.user_ids.user_id}/organizations` | `*` | -| `Get` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}` | | -| `List` | `GET` | `/api/v3/organizations` | | -| `List` | `GET` | `/api/v3/users/{collaborator.user_ids.user_id}/organizations` | | -| `Update` | `PUT` | `/api/v3/organizations/{organization.ids.organization_id}` | `*` | -| `Delete` | `DELETE` | `/api/v3/organizations/{organization_id}` | | +### Message `UpdatePacketBrokerGatewayResponse` -## File `lorawan-stack/api/packetbrokeragent.proto` +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `online_ttl` | [`google.protobuf.Duration`](#google.protobuf.Duration) | | Time to live of the online status. | ### Service `GsPba` @@ -5679,6 +8381,7 @@ The GsPba service connects a Gateway Server to a Packet Broker Agent. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `PublishUplink` | [`GatewayUplinkMessage`](#ttn.lorawan.v3.GatewayUplinkMessage) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | | +| `UpdateGateway` | [`UpdatePacketBrokerGatewayRequest`](#ttn.lorawan.v3.UpdatePacketBrokerGatewayRequest) | [`UpdatePacketBrokerGatewayResponse`](#ttn.lorawan.v3.UpdatePacketBrokerGatewayResponse) | Update the gateway, changing the fields specified by the field mask to the provided values. To mark a gateway as online, call this rpc setting online to true, include status_public in field_mask and keep calling this rpc before the returned online_ttl passes to keep the gateway online. | ### Service `NsPba` @@ -5688,6 +8391,58 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | ----------- | ------------ | ------------- | ------------| | `PublishDownlink` | [`DownlinkMessage`](#ttn.lorawan.v3.DownlinkMessage) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | PublishDownlink instructs the Packet Broker Agent to publish a downlink message to Packet Broker Router. | +### Service `Pba` + +The Pba service allows clients to manage peering through Packet Broker. + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| `GetInfo` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`PacketBrokerInfo`](#ttn.lorawan.v3.PacketBrokerInfo) | Get information about the Packet Broker registration. Viewing Packet Packet information requires administrative access. | +| `Register` | [`PacketBrokerRegisterRequest`](#ttn.lorawan.v3.PacketBrokerRegisterRequest) | [`PacketBrokerNetwork`](#ttn.lorawan.v3.PacketBrokerNetwork) | Register with Packet Broker. If no registration exists, it will be created. Any existing registration will be updated. Registration settings not in the request message are taken from Packet Broker Agent configuration and caller context. Packet Broker registration requires administrative access. Packet Broker registration is only supported for tenants and requires Packet Broker Agent to be configured with NetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled. | +| `Deregister` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Deregister from Packet Broker. Packet Broker deregistration requires administrative access. Packet Broker deregistration is only supported for tenants and requires Packet Broker Agent to be configured with NetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled. | +| `GetHomeNetworkDefaultRoutingPolicy` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`PacketBrokerDefaultRoutingPolicy`](#ttn.lorawan.v3.PacketBrokerDefaultRoutingPolicy) | Get the default routing policy. Getting routing policies requires administrative access. | +| `SetHomeNetworkDefaultRoutingPolicy` | [`SetPacketBrokerDefaultRoutingPolicyRequest`](#ttn.lorawan.v3.SetPacketBrokerDefaultRoutingPolicyRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Set the default routing policy. Setting routing policies requires administrative access. | +| `DeleteHomeNetworkDefaultRoutingPolicy` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Deletes the default routing policy. Deleting routing policies requires administrative access. | +| `ListHomeNetworkRoutingPolicies` | [`ListHomeNetworkRoutingPoliciesRequest`](#ttn.lorawan.v3.ListHomeNetworkRoutingPoliciesRequest) | [`PacketBrokerRoutingPolicies`](#ttn.lorawan.v3.PacketBrokerRoutingPolicies) | List the routing policies that Packet Broker Agent as Forwarder configured with Home Networks. Listing routing policies requires administrative access. | +| `GetHomeNetworkRoutingPolicy` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | [`PacketBrokerRoutingPolicy`](#ttn.lorawan.v3.PacketBrokerRoutingPolicy) | Get the routing policy for the given Home Network. Getting routing policies requires administrative access. | +| `SetHomeNetworkRoutingPolicy` | [`SetPacketBrokerRoutingPolicyRequest`](#ttn.lorawan.v3.SetPacketBrokerRoutingPolicyRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Set the routing policy for the given Home Network. Setting routing policies requires administrative access. | +| `DeleteHomeNetworkRoutingPolicy` | [`PacketBrokerNetworkIdentifier`](#ttn.lorawan.v3.PacketBrokerNetworkIdentifier) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the routing policy for the given Home Network. Deleting routing policies requires administrative access. | +| `GetHomeNetworkDefaultGatewayVisibility` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`PacketBrokerDefaultGatewayVisibility`](#ttn.lorawan.v3.PacketBrokerDefaultGatewayVisibility) | Get the default gateway visibility. Getting gateway visibilities requires administrative access. | +| `SetHomeNetworkDefaultGatewayVisibility` | [`SetPacketBrokerDefaultGatewayVisibilityRequest`](#ttn.lorawan.v3.SetPacketBrokerDefaultGatewayVisibilityRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Set the default gateway visibility. Setting gateway visibilities requires administrative access. | +| `DeleteHomeNetworkDefaultGatewayVisibility` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Deletes the default gateway visibility. Deleting gateway visibilities requires administrative access. | +| `ListNetworks` | [`ListPacketBrokerNetworksRequest`](#ttn.lorawan.v3.ListPacketBrokerNetworksRequest) | [`PacketBrokerNetworks`](#ttn.lorawan.v3.PacketBrokerNetworks) | List all listed networks. Listing networks requires administrative access. | +| `ListHomeNetworks` | [`ListPacketBrokerHomeNetworksRequest`](#ttn.lorawan.v3.ListPacketBrokerHomeNetworksRequest) | [`PacketBrokerNetworks`](#ttn.lorawan.v3.PacketBrokerNetworks) | List the listed home networks for which routing policies can be configured. Listing home networks requires administrative access. | +| `ListForwarderRoutingPolicies` | [`ListForwarderRoutingPoliciesRequest`](#ttn.lorawan.v3.ListForwarderRoutingPoliciesRequest) | [`PacketBrokerRoutingPolicies`](#ttn.lorawan.v3.PacketBrokerRoutingPolicies) | List the routing policies that Forwarders configured with Packet Broker Agent as Home Network. Listing routing policies requires administrative access. | + +#### HTTP bindings + +| Method Name | Method | Pattern | Body | +| ----------- | ------ | ------- | ---- | +| `GetInfo` | `GET` | `/api/v3/pba/info` | | +| `Register` | `PUT` | `/api/v3/pba/registration` | `*` | +| `Register` | `POST` | `/api/v3/pba/registration` | `*` | +| `Deregister` | `DELETE` | `/api/v3/pba/registration` | | +| `GetHomeNetworkDefaultRoutingPolicy` | `GET` | `/api/v3/pba/home-networks/policies/default` | | +| `SetHomeNetworkDefaultRoutingPolicy` | `PUT` | `/api/v3/pba/home-networks/policies/default` | `*` | +| `SetHomeNetworkDefaultRoutingPolicy` | `POST` | `/api/v3/pba/home-networks/policies/default` | `*` | +| `DeleteHomeNetworkDefaultRoutingPolicy` | `DELETE` | `/api/v3/pba/home-networks/policies/default` | | +| `ListHomeNetworkRoutingPolicies` | `GET` | `/api/v3/pba/home-networks/policies` | | +| `GetHomeNetworkRoutingPolicy` | `GET` | `/api/v3/pba/home-networks/policies/{net_id}` | | +| `GetHomeNetworkRoutingPolicy` | `GET` | `/api/v3/pba/home-networks/policies/{net_id}/{tenant_id}` | | +| `SetHomeNetworkRoutingPolicy` | `PUT` | `/api/v3/pba/home-networks/policies/{home_network_id.net_id}` | `*` | +| `SetHomeNetworkRoutingPolicy` | `POST` | `/api/v3/pba/home-networks/policies/{home_network_id.net_id}` | `*` | +| `SetHomeNetworkRoutingPolicy` | `PUT` | `/api/v3/pba/home-networks/policies/{home_network_id.net_id}/{home_network_id.tenant_id}` | `*` | +| `SetHomeNetworkRoutingPolicy` | `POST` | `/api/v3/pba/home-networks/policies/{home_network_id.net_id}/{home_network_id.tenant_id}` | `*` | +| `DeleteHomeNetworkRoutingPolicy` | `DELETE` | `/api/v3/pba/home-networks/policies/{net_id}` | | +| `DeleteHomeNetworkRoutingPolicy` | `DELETE` | `/api/v3/pba/home-networks/policies/{net_id}/{tenant_id}` | | +| `GetHomeNetworkDefaultGatewayVisibility` | `GET` | `/api/v3/pba/home-networks/gateway-visibilities/default` | | +| `SetHomeNetworkDefaultGatewayVisibility` | `PUT` | `/api/v3/pba/home-networks/gateway-visibilities/default` | `*` | +| `SetHomeNetworkDefaultGatewayVisibility` | `POST` | `/api/v3/pba/home-networks/gateway-visibilities/default` | `*` | +| `DeleteHomeNetworkDefaultGatewayVisibility` | `DELETE` | `/api/v3/pba/home-networks/gateway-visibilities/default` | | +| `ListNetworks` | `GET` | `/api/v3/pba/networks` | | +| `ListHomeNetworks` | `GET` | `/api/v3/pba/home-networks` | | +| `ListForwarderRoutingPolicies` | `GET` | `/api/v3/pba/forwarders/policies` | | + ## File `lorawan-stack/api/picture.proto` ### Message `Picture` @@ -5710,6 +8465,13 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | `mime_type` | [`string`](#string) | | MIME type of the picture. | | `data` | [`bytes`](#bytes) | | Picture data. A data URI can be constructed as follows: `data:;base64,`. | +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `mime_type` |

`string.max_len`: `32`

| +| `data` |

`bytes.max_len`: `8388608`

| + ### Message `Picture.SizesEntry` | Field | Type | Label | Description | @@ -5723,9 +8485,9 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `format_id` | [`string`](#string) | | | -| `end_device` | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | | | -| `image` | [`GenerateEndDeviceQRCodeRequest.Image`](#ttn.lorawan.v3.GenerateEndDeviceQRCodeRequest.Image) | | | +| `format_id` | [`string`](#string) | | QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service. | +| `end_device` | [`EndDevice`](#ttn.lorawan.v3.EndDevice) | | End device to use as input to generate the QR code. | +| `image` | [`GenerateEndDeviceQRCodeRequest.Image`](#ttn.lorawan.v3.GenerateEndDeviceQRCodeRequest.Image) | | If set, the server will render the QR code image according to these settings. | #### Field Rules @@ -5738,7 +8500,7 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `image_size` | [`uint32`](#uint32) | | | +| `image_size` | [`uint32`](#uint32) | | Requested QR code image dimension in pixels. | #### Field Rules @@ -5750,14 +8512,14 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `text` | [`string`](#string) | | | +| `text` | [`string`](#string) | | Text representation of the QR code contents. | | `image` | [`Picture`](#ttn.lorawan.v3.Picture) | | QR code in PNG format, if requested. | ### Message `GetQRCodeFormatRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `format_id` | [`string`](#string) | | | +| `format_id` | [`string`](#string) | | QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service. | #### Field Rules @@ -5765,6 +8527,27 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | ----- | ----------- | | `format_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +### Message `ParseEndDeviceQRCodeRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `format_id` | [`string`](#string) | | QR code format identifier. Enumerate available formats with the rpc `ListFormats`. If this field is not specified, the server will attempt to parse the data with each known format. | +| `qr_code` | [`bytes`](#bytes) | | Raw QR code contents. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `format_id` |

`string.max_len`: `36`

`string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$`

| +| `qr_code` |

`bytes.min_len`: `10`

`bytes.max_len`: `1024`

| + +### Message `ParseEndDeviceQRCodeResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `format_id` | [`string`](#string) | | Identifier of the format used to successfully parse the QR code data. | +| `end_device_template` | [`EndDeviceTemplate`](#ttn.lorawan.v3.EndDeviceTemplate) | | | + ### Message `QRCodeFormat` | Field | Type | Label | Description | @@ -5784,7 +8567,7 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `formats` | [`QRCodeFormats.FormatsEntry`](#ttn.lorawan.v3.QRCodeFormats.FormatsEntry) | repeated | | +| `formats` | [`QRCodeFormats.FormatsEntry`](#ttn.lorawan.v3.QRCodeFormats.FormatsEntry) | repeated | Available formats. The map key is the format identifier. | #### Field Rules @@ -5806,6 +8589,7 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | `GetFormat` | [`GetQRCodeFormatRequest`](#ttn.lorawan.v3.GetQRCodeFormatRequest) | [`QRCodeFormat`](#ttn.lorawan.v3.QRCodeFormat) | Return the QR code format. | | `ListFormats` | [`.google.protobuf.Empty`](#google.protobuf.Empty) | [`QRCodeFormats`](#ttn.lorawan.v3.QRCodeFormats) | Returns the supported formats. | | `Generate` | [`GenerateEndDeviceQRCodeRequest`](#ttn.lorawan.v3.GenerateEndDeviceQRCodeRequest) | [`GenerateQRCodeResponse`](#ttn.lorawan.v3.GenerateQRCodeResponse) | Generates a QR code. | +| `Parse` | [`ParseEndDeviceQRCodeRequest`](#ttn.lorawan.v3.ParseEndDeviceQRCodeRequest) | [`ParseEndDeviceQRCodeResponse`](#ttn.lorawan.v3.ParseEndDeviceQRCodeResponse) | Parse QR Codes of known formats and return the information contained within. | #### HTTP bindings @@ -5814,6 +8598,8 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | `GetFormat` | `GET` | `/api/v3/qr-codes/end-devices/formats/{format_id}` | | | `ListFormats` | `GET` | `/api/v3/qr-codes/end-devices/formats` | | | `Generate` | `POST` | `/api/v3/qr-codes/end-devices` | `*` | +| `Parse` | `POST` | `/api/v3/qr-code/end-devices/parse` | `*` | +| `Parse` | `POST` | `/api/v3/qr-code/end-devices/{format_id}/parse` | `*` | ## File `lorawan-stack/api/regional.proto` @@ -5870,6 +8656,9 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | `key` | [`string`](#string) | | Immutable and unique secret value of the API key. Generated by the Access Server. | | `name` | [`string`](#string) | | User-defined (friendly) name for the API key. | | `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | Rights that are granted to this API key. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | #### Field Rules @@ -5877,6 +8666,7 @@ The NsPba service connects a Network Server to a Packet Broker Agent. | ----- | ----------- | | `name` |

`string.max_len`: `50`

| | `rights` |

`repeated.items.enum.defined_only`: `true`

| +| `expires_at` |

`timestamp.gt_now`: `true`

| ### Message `APIKeys` @@ -5943,6 +8733,7 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_USER_CLIENTS_CREATE` | 11 | The right to create an OAuth client under the account of the user. | | `RIGHT_USER_ORGANIZATIONS_LIST` | 12 | The right to list organizations the user is a member of. | | `RIGHT_USER_ORGANIZATIONS_CREATE` | 13 | The right to create an organization under the user account. | +| `RIGHT_USER_NOTIFICATIONS_READ` | 59 | The right to read notifications sent to the user. | | `RIGHT_USER_ALL` | 14 | The pseudo-right for all (current and future) user rights. | | `RIGHT_APPLICATION_INFO` | 15 | The right to view application information. | | `RIGHT_APPLICATION_SETTINGS_BASIC` | 16 | The right to edit basic application settings. | @@ -5957,9 +8748,13 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_APPLICATION_TRAFFIC_READ` | 24 | The right to read application traffic (uplink and downlink). | | `RIGHT_APPLICATION_TRAFFIC_UP_WRITE` | 25 | The right to write uplink application traffic. | | `RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE` | 26 | The right to write downlink application traffic. | -| `RIGHT_APPLICATION_LINK` | 27 | The right to link as Application to a Network Server for traffic exchange, i.e. read uplink and write downlink (API keys only). This right is typically only given to an Application Server. This right implies RIGHT_APPLICATION_INFO. | +| `RIGHT_APPLICATION_LINK` | 27 | The right to link as Application to a Network Server for traffic exchange, i.e. read uplink and write downlink (API keys only). This right is typically only given to an Application Server. This right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ, and RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE. | | `RIGHT_APPLICATION_ALL` | 28 | The pseudo-right for all (current and future) application rights. | | `RIGHT_CLIENT_ALL` | 29 | The pseudo-right for all (current and future) OAuth client rights. | +| `RIGHT_CLIENT_INFO` | 60 | The right to read client information. | +| `RIGHT_CLIENT_SETTINGS_BASIC` | 61 | The right to edit basic client settings. | +| `RIGHT_CLIENT_SETTINGS_COLLABORATORS` | 62 | The right to view and edit client collaborators. | +| `RIGHT_CLIENT_DELETE` | 63 | The right to delete a client. | | `RIGHT_GATEWAY_INFO` | 30 | The right to view gateway information. | | `RIGHT_GATEWAY_SETTINGS_BASIC` | 31 | The right to edit basic gateway settings. | | `RIGHT_GATEWAY_SETTINGS_API_KEYS` | 32 | The right to view and edit gateway API keys. | @@ -5970,6 +8765,8 @@ Right is the enum that defines all the different rights to do something in the n | `RIGHT_GATEWAY_LINK` | 37 | The right to link as Gateway to a Gateway Server for traffic exchange, i.e. write uplink and read downlink (API keys only) This right is typically only given to a gateway. This right implies RIGHT_GATEWAY_INFO. | | `RIGHT_GATEWAY_STATUS_READ` | 38 | The right to view gateway status. | | `RIGHT_GATEWAY_LOCATION_READ` | 39 | The right to view view gateway location. | +| `RIGHT_GATEWAY_WRITE_SECRETS` | 57 | The right to store secrets associated with this gateway. | +| `RIGHT_GATEWAY_READ_SECRETS` | 58 | The right to retrieve secrets associated with this gateway. | | `RIGHT_GATEWAY_ALL` | 40 | The pseudo-right for all (current and future) gateway rights. | | `RIGHT_ORGANIZATION_INFO` | 41 | The right to view organization information. | | `RIGHT_ORGANIZATION_SETTINGS_BASIC` | 42 | The right to edit basic organization settings. | @@ -5989,11 +8786,109 @@ Right is the enum that defines all the different rights to do something in the n ## File `lorawan-stack/api/search_services.proto` +### Message `SearchAccountsRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `query` | [`string`](#string) | | | +| `only_users` | [`bool`](#bool) | | | +| `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `client_ids` | [`ClientIdentifiers`](#ttn.lorawan.v3.ClientIdentifiers) | | | +| `gateway_ids` | [`GatewayIdentifiers`](#ttn.lorawan.v3.GatewayIdentifiers) | | | +| `organization_ids` | [`OrganizationIdentifiers`](#ttn.lorawan.v3.OrganizationIdentifiers) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `query` |

`string.max_len`: `50`

| + +### Message `SearchAccountsResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `account_ids` | [`OrganizationOrUserIdentifiers`](#ttn.lorawan.v3.OrganizationOrUserIdentifiers) | repeated | | + +### Message `SearchApplicationsRequest` + +This message is used for finding applications in the EntityRegistrySearch service. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `query` | [`string`](#string) | | Find applications where the ID, name or description contains this substring. | +| `id_contains` | [`string`](#string) | | Find applications where the ID contains this substring. | +| `name_contains` | [`string`](#string) | | Find applications where the name contains this substring. | +| `description_contains` | [`string`](#string) | | Find applications where the description contains this substring. | +| `attributes_contain` | [`SearchApplicationsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchApplicationsRequest.AttributesContainEntry) | repeated | Find applications where the given attributes contain these substrings. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted applications. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `order` |

`string.in`: `[ application_id -application_id name -name created_at -created_at]`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `SearchApplicationsRequest.AttributesContainEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `SearchClientsRequest` + +This message is used for finding OAuth clients in the EntityRegistrySearch service. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `query` | [`string`](#string) | | Find OAuth clients where the ID, name or description contains this substring. | +| `id_contains` | [`string`](#string) | | Find OAuth clients where the ID contains this substring. | +| `name_contains` | [`string`](#string) | | Find OAuth clients where the name contains this substring. | +| `description_contains` | [`string`](#string) | | Find OAuth clients where the description contains this substring. | +| `attributes_contain` | [`SearchClientsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchClientsRequest.AttributesContainEntry) | repeated | Find OAuth clients where the given attributes contain these substrings. | +| `state` | [`State`](#ttn.lorawan.v3.State) | repeated | Find OAuth clients where the state is any of these states. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted OAuth clients. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `state` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `order` |

`string.in`: `[ client_id -client_id name -name created_at -created_at]`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `SearchClientsRequest.AttributesContainEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + ### Message `SearchEndDevicesRequest` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `application_ids` | [`ApplicationIdentifiers`](#ttn.lorawan.v3.ApplicationIdentifiers) | | | +| `query` | [`string`](#string) | | Find end devices where the ID, name, description or EUI contains this substring. | | `id_contains` | [`string`](#string) | | Find end devices where the ID contains this substring. | | `name_contains` | [`string`](#string) | | Find end devices where the name contains this substring. | | `description_contains` | [`string`](#string) | | Find end devices where the description contains this substring. | @@ -6011,7 +8906,15 @@ Right is the enum that defines all the different rights to do something in the n | Field | Validations | | ----- | ----------- | | `application_ids` |

`message.required`: `true`

| -| `attributes_contain` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `dev_eui_contains` |

`string.max_len`: `16`

| +| `join_eui_contains` |

`string.max_len`: `16`

| +| `dev_addr_contains` |

`string.max_len`: `8`

| +| `order` |

`string.in`: `[ device_id -device_id join_eui -join_eui dev_eui -dev_eui name -name description -description created_at -created_at]`

| | `limit` |

`uint32.lte`: `1000`

| ### Message `SearchEndDevicesRequest.AttributesContainEntry` @@ -6021,29 +8924,112 @@ Right is the enum that defines all the different rights to do something in the n | `key` | [`string`](#string) | | | | `value` | [`string`](#string) | | | -### Message `SearchEntitiesRequest` +### Message `SearchGatewaysRequest` + +This message is used for finding gateways in the EntityRegistrySearch service. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `query` | [`string`](#string) | | Find gateways where the ID, name, description or EUI contains this substring. | +| `id_contains` | [`string`](#string) | | Find gateways where the ID contains this substring. | +| `name_contains` | [`string`](#string) | | Find gateways where the name contains this substring. | +| `description_contains` | [`string`](#string) | | Find gateways where the description contains this substring. | +| `attributes_contain` | [`SearchGatewaysRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchGatewaysRequest.AttributesContainEntry) | repeated | Find gateways where the given attributes contain these substrings. | +| `eui_contains` | [`string`](#string) | | Find gateways where the (hexadecimal) EUI contains this substring. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted gateways. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `eui_contains` |

`string.max_len`: `16`

| +| `order` |

`string.in`: `[ gateway_id -gateway_id gateway_eui -gateway_eui name -name created_at -created_at]`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `SearchGatewaysRequest.AttributesContainEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `SearchOrganizationsRequest` + +This message is used for finding organizations in the EntityRegistrySearch service. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `query` | [`string`](#string) | | Find organizations where the ID, name or description contains this substring. | +| `id_contains` | [`string`](#string) | | Find organizations where the ID contains this substring. | +| `name_contains` | [`string`](#string) | | Find organizations where the name contains this substring. | +| `description_contains` | [`string`](#string) | | Find organizations where the description contains this substring. | +| `attributes_contain` | [`SearchOrganizationsRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchOrganizationsRequest.AttributesContainEntry) | repeated | Find organizations where the given attributes contain these substrings. | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | +| `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | +| `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | +| `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted organizations. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `order` |

`string.in`: `[ organization_id -organization_id name -name created_at -created_at]`

| +| `limit` |

`uint32.lte`: `1000`

| + +### Message `SearchOrganizationsRequest.AttributesContainEntry` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key` | [`string`](#string) | | | +| `value` | [`string`](#string) | | | + +### Message `SearchUsersRequest` -This message is used for finding entities in the EntityRegistrySearch service. +This message is used for finding users in the EntityRegistrySearch service. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `id_contains` | [`string`](#string) | | Find entities where the ID contains this substring. | -| `name_contains` | [`string`](#string) | | Find entities where the name contains this substring. | -| `description_contains` | [`string`](#string) | | Find entities where the description contains this substring. | -| `attributes_contain` | [`SearchEntitiesRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchEntitiesRequest.AttributesContainEntry) | repeated | Find entities where the given attributes contain these substrings. | +| `query` | [`string`](#string) | | Find users where the ID, name or description contains this substring. | +| `id_contains` | [`string`](#string) | | Find users where the ID contains this substring. | +| `name_contains` | [`string`](#string) | | Find users where the name contains this substring. | +| `description_contains` | [`string`](#string) | | Find users where the description contains this substring. | +| `attributes_contain` | [`SearchUsersRequest.AttributesContainEntry`](#ttn.lorawan.v3.SearchUsersRequest.AttributesContainEntry) | repeated | Find users where the given attributes contain these substrings. | +| `state` | [`State`](#ttn.lorawan.v3.State) | repeated | Find users where the state is any of these states. | | `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | | | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted users. | #### Field Rules | Field | Validations | | ----- | ----------- | -| `attributes_contain` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `query` |

`string.max_len`: `50`

| +| `id_contains` |

`string.max_len`: `50`

| +| `name_contains` |

`string.max_len`: `50`

| +| `description_contains` |

`string.max_len`: `50`

| +| `attributes_contain` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `50`

| +| `state` |

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `order` |

`string.in`: `[ user_id -user_id name -name primary_email_address -primary_email_address state -state admin -admin created_at -created_at]`

| | `limit` |

`uint32.lte`: `1000`

| -### Message `SearchEntitiesRequest.AttributesContainEntry` +### Message `SearchUsersRequest.AttributesContainEntry` | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | @@ -6058,7 +9044,7 @@ This service is not implemented on all deployments. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `SearchEndDevices` | [`SearchEndDevicesRequest`](#ttn.lorawan.v3.SearchEndDevicesRequest) | [`EndDevices`](#ttn.lorawan.v3.EndDevices) | | +| `SearchEndDevices` | [`SearchEndDevicesRequest`](#ttn.lorawan.v3.SearchEndDevicesRequest) | [`EndDevices`](#ttn.lorawan.v3.EndDevices) | Search for end devices in the given application that match the conditions specified in the request. | #### HTTP bindings @@ -6074,11 +9060,12 @@ This service is not implemented on all deployments. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| `SearchApplications` | [`SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) | [`Applications`](#ttn.lorawan.v3.Applications) | | -| `SearchClients` | [`SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) | [`Clients`](#ttn.lorawan.v3.Clients) | | -| `SearchGateways` | [`SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) | [`Gateways`](#ttn.lorawan.v3.Gateways) | | -| `SearchOrganizations` | [`SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) | [`Organizations`](#ttn.lorawan.v3.Organizations) | | -| `SearchUsers` | [`SearchEntitiesRequest`](#ttn.lorawan.v3.SearchEntitiesRequest) | [`Users`](#ttn.lorawan.v3.Users) | | +| `SearchApplications` | [`SearchApplicationsRequest`](#ttn.lorawan.v3.SearchApplicationsRequest) | [`Applications`](#ttn.lorawan.v3.Applications) | Search for applications that match the conditions specified in the request. Non-admin users will only match applications that they have rights on. | +| `SearchClients` | [`SearchClientsRequest`](#ttn.lorawan.v3.SearchClientsRequest) | [`Clients`](#ttn.lorawan.v3.Clients) | Search for OAuth clients that match the conditions specified in the request. Non-admin users will only match OAuth clients that they have rights on. | +| `SearchGateways` | [`SearchGatewaysRequest`](#ttn.lorawan.v3.SearchGatewaysRequest) | [`Gateways`](#ttn.lorawan.v3.Gateways) | Search for gateways that match the conditions specified in the request. Non-admin users will only match gateways that they have rights on. | +| `SearchOrganizations` | [`SearchOrganizationsRequest`](#ttn.lorawan.v3.SearchOrganizationsRequest) | [`Organizations`](#ttn.lorawan.v3.Organizations) | Search for organizations that match the conditions specified in the request. Non-admin users will only match organizations that they have rights on. | +| `SearchUsers` | [`SearchUsersRequest`](#ttn.lorawan.v3.SearchUsersRequest) | [`Users`](#ttn.lorawan.v3.Users) | Search for users that match the conditions specified in the request. This is only available to admin users. | +| `SearchAccounts` | [`SearchAccountsRequest`](#ttn.lorawan.v3.SearchAccountsRequest) | [`SearchAccountsResponse`](#ttn.lorawan.v3.SearchAccountsResponse) | Search for accounts that match the conditions specified in the request. | #### HTTP bindings @@ -6089,9 +9076,50 @@ This service is not implemented on all deployments. | `SearchGateways` | `GET` | `/api/v3/search/gateways` | | | `SearchOrganizations` | `GET` | `/api/v3/search/organizations` | | | `SearchUsers` | `GET` | `/api/v3/search/users` | | +| `SearchAccounts` | `GET` | `/api/v3/search/accounts` | | +| `SearchAccounts` | `GET` | `/api/v3/applications/{application_ids.application_id}/collaborators/search` | | +| `SearchAccounts` | `GET` | `/api/v3/clients/{client_ids.client_id}/collaborators/search` | | +| `SearchAccounts` | `GET` | `/api/v3/gateways/{gateway_ids.gateway_id}/collaborators/search` | | +| `SearchAccounts` | `GET` | `/api/v3/organizations/{organization_ids.organization_id}/collaborators/search` | | + +## File `lorawan-stack/api/secrets.proto` + +### Message `Secret` + +Secret contains a secret value. It also contains the ID of the Encryption key used to encrypt it. + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `key_id` | [`string`](#string) | | ID of the Key used to encrypt the secret. | +| `value` | [`bytes`](#bytes) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `value` |

`bytes.max_len`: `2048`

| ## File `lorawan-stack/api/user.proto` +### Message `CreateLoginTokenRequest` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `user_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | +| `skip_email` | [`bool`](#bool) | | Skip sending the login token to the user by email. This field is only effective when the login token is created by an admin user. | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `user_ids` |

`message.required`: `true`

| + +### Message `CreateLoginTokenResponse` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `token` | [`string`](#string) | | The token that can be used for logging in as the user. This field is only present if a token was created by an admin user for a non-admin user. | + ### Message `CreateTemporaryPasswordRequest` | Field | Type | Label | Description | @@ -6111,6 +9139,7 @@ This service is not implemented on all deployments. | `user_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | | `name` | [`string`](#string) | | | | `rights` | [`Right`](#ttn.lorawan.v3.Right) | repeated | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | #### Field Rules @@ -6118,7 +9147,8 @@ This service is not implemented on all deployments. | ----- | ----------- | | `user_ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| -| `rights` |

`repeated.items.enum.defined_only`: `true`

| +| `rights` |

`repeated.min_items`: `1`

`repeated.unique`: `true`

`repeated.items.enum.defined_only`: `true`

| +| `expires_at` |

`timestamp.gt_now`: `true`

| ### Message `CreateUserRequest` @@ -6213,6 +9243,7 @@ This service is not implemented on all deployments. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `user_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | +| `order` | [`string`](#string) | | Order the results by this field path. Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | @@ -6221,6 +9252,7 @@ This service is not implemented on all deployments. | Field | Validations | | ----- | ----------- | | `user_ids` |

`message.required`: `true`

| +| `order` |

`string.in`: `[ api_key_id -api_key_id name -name created_at -created_at expires_at -expires_at]`

| | `limit` |

`uint32.lte`: `1000`

| ### Message `ListUserSessionsRequest` @@ -6248,6 +9280,7 @@ This service is not implemented on all deployments. | `order` | [`string`](#string) | | Order the results by this field path (must be present in the field mask). Default ordering is by ID. Prepend with a minus (-) to reverse the order. | | `limit` | [`uint32`](#uint32) | | Limit the number of results per page. | | `page` | [`uint32`](#uint32) | | Page number for pagination. 0 is interpreted as 1. | +| `deleted` | [`bool`](#bool) | | Only return recently deleted users. | #### Field Rules @@ -6256,6 +9289,23 @@ This service is not implemented on all deployments. | `order` |

`string.in`: `[ user_id -user_id name -name primary_email_address -primary_email_address state -state admin -admin created_at -created_at]`

| | `limit` |

`uint32.lte`: `1000`

| +### Message `LoginToken` + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `user_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | +| `token` | [`string`](#string) | | | +| `used` | [`bool`](#bool) | | | + +#### Field Rules + +| Field | Validations | +| ----- | ----------- | +| `user_ids` |

`message.required`: `true`

| + ### Message `SendInvitationRequest` | Field | Type | Label | Description | @@ -6274,6 +9324,7 @@ This service is not implemented on all deployments. | ----- | ---- | ----- | ----------- | | `user_ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | | `api_key` | [`APIKey`](#ttn.lorawan.v3.APIKey) | | | +| `field_mask` | [`google.protobuf.FieldMask`](#google.protobuf.FieldMask) | | The names of the api key fields that should be updated. | #### Field Rules @@ -6296,6 +9347,8 @@ This service is not implemented on all deployments. | Field | Validations | | ----- | ----------- | | `user_ids` |

`message.required`: `true`

| +| `new` |

`string.max_len`: `1000`

| +| `old` |

`string.max_len`: `1000`

| ### Message `UpdateUserRequest` @@ -6316,24 +9369,26 @@ User is the message that defines a user on the network. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| `ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | | -| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `name` | [`string`](#string) | | | -| `description` | [`string`](#string) | | | +| `ids` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | | The identifiers of the user. These are public and can be seen by any authenticated user in the network. | +| `created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the user was created. This information is public and can be seen by any authenticated user in the network. | +| `updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the user was last updated. This information is public and can be seen by any authenticated user in the network. | +| `deleted_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the user was deleted. This information is public and can be seen by any authenticated user in the network. | +| `name` | [`string`](#string) | | The name of the user. This information is public and can be seen by any authenticated user in the network. | +| `description` | [`string`](#string) | | A description for the user. This information is public and can be seen by any authenticated user in the network. | | `attributes` | [`User.AttributesEntry`](#ttn.lorawan.v3.User.AttributesEntry) | repeated | Key-value attributes for this users. Typically used for storing integration-specific data. | -| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user. | +| `contact_info` | [`ContactInfo`](#ttn.lorawan.v3.ContactInfo) | repeated | Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user. This field is deprecated. | | `primary_email_address` | [`string`](#string) | | Primary email address that can be used for logging in. This address is not public, use contact_info for that. | | `primary_email_address_validated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | When the primary email address was validated. Note that email address validation is not required on all networks. | | `password` | [`string`](#string) | | The password field is only considered when creating a user. It is not returned on API calls, and can not be updated by updating the User. See the UpdatePassword method of the UserRegistry service for more information. | | `password_updated_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | | `require_password_update` | [`bool`](#bool) | | | -| `state` | [`State`](#ttn.lorawan.v3.State) | | The reviewing state of the user. This field can only be modified by admins. | -| `admin` | [`bool`](#bool) | | This user is an admin. This field can only be modified by other admins. | +| `state` | [`State`](#ttn.lorawan.v3.State) | | The reviewing state of the user. This information is public and can be seen by any authenticated user in the network. This field can only be modified by admins. | +| `state_description` | [`string`](#string) | | A description for the state field. This field can only be modified by admins, and should typically only be updated when also updating `state`. | +| `admin` | [`bool`](#bool) | | This user is an admin. This information is public and can be seen by any authenticated user in the network. This field can only be modified by other admins. | | `temporary_password` | [`string`](#string) | | The temporary password can only be used to update a user's password; never returned on API calls. It is not returned on API calls, and can not be updated by updating the User. See the CreateTemporaryPassword method of the UserRegistry service for more information. | | `temporary_password_created_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | | `temporary_password_expires_at` | [`google.protobuf.Timestamp`](#google.protobuf.Timestamp) | | | -| `profile_picture` | [`Picture`](#ttn.lorawan.v3.Picture) | | | +| `profile_picture` | [`Picture`](#ttn.lorawan.v3.Picture) | | A profile picture for the user. This information is public and can be seen by any authenticated user in the network. | #### Field Rules @@ -6342,9 +9397,13 @@ User is the message that defines a user on the network. | `ids` |

`message.required`: `true`

| | `name` |

`string.max_len`: `50`

| | `description` |

`string.max_len`: `2000`

| -| `attributes` |

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

| +| `attributes` |

`map.max_pairs`: `10`

`map.keys.string.max_len`: `36`

`map.keys.string.pattern`: `^[a-z0-9](?:[-]?[a-z0-9]){2,}$`

`map.values.string.max_len`: `200`

| +| `contact_info` |

`repeated.max_items`: `10`

| | `primary_email_address` |

`string.email`: `true`

| +| `password` |

`string.max_len`: `1000`

| | `state` |

`enum.defined_only`: `true`

| +| `state_description` |

`string.max_len`: `128`

| +| `temporary_password` |

`string.max_len`: `1000`

| ### Message `User.AttributesEntry` @@ -6401,6 +9460,9 @@ User is the message that defines a user on the network. ### Service `UserAccess` +The UserAcces service, exposed by the Identity Server, is used to manage +API keys of users. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `ListRights` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | [`Rights`](#ttn.lorawan.v3.Rights) | List the rights the caller has on this user. | @@ -6408,6 +9470,7 @@ User is the message that defines a user on the network. | `ListAPIKeys` | [`ListUserAPIKeysRequest`](#ttn.lorawan.v3.ListUserAPIKeysRequest) | [`APIKeys`](#ttn.lorawan.v3.APIKeys) | List the API keys for this user. | | `GetAPIKey` | [`GetUserAPIKeyRequest`](#ttn.lorawan.v3.GetUserAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Get a single API key of this user. | | `UpdateAPIKey` | [`UpdateUserAPIKeyRequest`](#ttn.lorawan.v3.UpdateUserAPIKeyRequest) | [`APIKey`](#ttn.lorawan.v3.APIKey) | Update the rights of an API key of the user. This method can also be used to delete the API key, by giving it no rights. The caller is required to have all assigned or/and removed rights. | +| `CreateLoginToken` | [`CreateLoginTokenRequest`](#ttn.lorawan.v3.CreateLoginTokenRequest) | [`CreateLoginTokenResponse`](#ttn.lorawan.v3.CreateLoginTokenResponse) | Create a login token that can be used for a one-time login as a user. | #### HTTP bindings @@ -6418,6 +9481,7 @@ User is the message that defines a user on the network. | `ListAPIKeys` | `GET` | `/api/v3/users/{user_ids.user_id}/api-keys` | | | `GetAPIKey` | `GET` | `/api/v3/users/{user_ids.user_id}/api-keys/{key_id}` | | | `UpdateAPIKey` | `PUT` | `/api/v3/users/{user_ids.user_id}/api-keys/{api_key.id}` | `*` | +| `CreateLoginToken` | `POST` | `/api/v3/users/{user_ids.user_id}/login-tokens` | | ### Service `UserInvitationRegistry` @@ -6437,6 +9501,9 @@ User is the message that defines a user on the network. ### Service `UserRegistry` +The UserRegistry service, exposed by the Identity Server, is used to manage +user registrations. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `Create` | [`CreateUserRequest`](#ttn.lorawan.v3.CreateUserRequest) | [`User`](#ttn.lorawan.v3.User) | Register a new user. This method may be restricted by network settings. | @@ -6446,6 +9513,10 @@ User is the message that defines a user on the network. | `CreateTemporaryPassword` | [`CreateTemporaryPasswordRequest`](#ttn.lorawan.v3.CreateTemporaryPasswordRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Create a temporary password that can be used for updating a forgotten password. The generated password is sent to the user's email address. | | `UpdatePassword` | [`UpdateUserPasswordRequest`](#ttn.lorawan.v3.UpdateUserPasswordRequest) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Update the password of the user. | | `Delete` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Delete the user. This may not release the user ID for reuse. | +| `Restore` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Restore a recently deleted user. + +Deployment configuration may specify if, and for how long after deletion, entities can be restored. | +| `Purge` | [`UserIdentifiers`](#ttn.lorawan.v3.UserIdentifiers) | [`.google.protobuf.Empty`](#google.protobuf.Empty) | Purge the user. This will release the user ID for reuse. The user is responsible for clearing data from any (external) integrations that may store and expose data by user or organization ID. | #### HTTP bindings @@ -6458,9 +9529,14 @@ User is the message that defines a user on the network. | `CreateTemporaryPassword` | `POST` | `/api/v3/users/{user_ids.user_id}/temporary_password` | | | `UpdatePassword` | `PUT` | `/api/v3/users/{user_ids.user_id}/password` | `*` | | `Delete` | `DELETE` | `/api/v3/users/{user_id}` | | +| `Restore` | `POST` | `/api/v3/users/{user_id}/restore` | | +| `Purge` | `DELETE` | `/api/v3/users/{user_id}/purge` | | ### Service `UserSessionRegistry` +The UserSessionRegistry service, exposed by the Identity Server, is used to manage +(browser) sessions of the user. + | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | `List` | [`ListUserSessionsRequest`](#ttn.lorawan.v3.ListUserSessionsRequest) | [`UserSessions`](#ttn.lorawan.v3.UserSessions) | List the active sessions for the given user. | diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.swagger.json b/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.swagger.json index 972df39b4..777357a48 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.swagger.json +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/api.swagger.json @@ -4,6 +4,173 @@ "title": "The Things Stack for LoRaWAN v3 API", "version": "version not set" }, + "tags": [ + { + "name": "ApplicationRegistry" + }, + { + "name": "ApplicationAccess" + }, + { + "name": "As" + }, + { + "name": "NsAs" + }, + { + "name": "AppAs" + }, + { + "name": "AsEndDeviceRegistry" + }, + { + "name": "ApplicationUpStorage" + }, + { + "name": "ApplicationPackageRegistry" + }, + { + "name": "ApplicationPubSubRegistry" + }, + { + "name": "ApplicationWebhookRegistry" + }, + { + "name": "ClientRegistry" + }, + { + "name": "ClientAccess" + }, + { + "name": "Configuration" + }, + { + "name": "ContactInfoRegistry" + }, + { + "name": "EndDeviceClaimingServer" + }, + { + "name": "GatewayClaimingServer" + }, + { + "name": "DeviceRepository" + }, + { + "name": "EndDeviceRegistry" + }, + { + "name": "EndDeviceTemplateConverter" + }, + { + "name": "Events" + }, + { + "name": "GatewayConfigurationService" + }, + { + "name": "GatewayRegistry" + }, + { + "name": "GatewayAccess" + }, + { + "name": "GatewayConfigurator" + }, + { + "name": "GtwGs" + }, + { + "name": "NsGs" + }, + { + "name": "Gs" + }, + { + "name": "EntityAccess" + }, + { + "name": "Is" + }, + { + "name": "NsJs" + }, + { + "name": "AsJs" + }, + { + "name": "AppJs" + }, + { + "name": "NetworkCryptoService" + }, + { + "name": "ApplicationCryptoService" + }, + { + "name": "JsEndDeviceRegistry" + }, + { + "name": "ApplicationActivationSettingRegistry" + }, + { + "name": "Js" + }, + { + "name": "Ns" + }, + { + "name": "AsNs" + }, + { + "name": "GsNs" + }, + { + "name": "NsEndDeviceRegistry" + }, + { + "name": "NotificationService" + }, + { + "name": "OAuthAuthorizationRegistry" + }, + { + "name": "OrganizationRegistry" + }, + { + "name": "OrganizationAccess" + }, + { + "name": "GsPba" + }, + { + "name": "NsPba" + }, + { + "name": "Pba" + }, + { + "name": "EndDeviceQRCodeGenerator" + }, + { + "name": "EntityRegistrySearch" + }, + { + "name": "EndDeviceRegistrySearch" + }, + { + "name": "UserRegistry" + }, + { + "name": "UserAccess" + }, + { + "name": "UserInvitationRegistry" + }, + { + "name": "UserSessionRegistry" + } + ], "consumes": [ "application/json" ], @@ -23,9 +190,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -52,15 +219,11 @@ "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", + "description": "The names of the application fields that should be returned.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { "name": "order", @@ -84,6 +247,13 @@ "required": false, "type": "integer", "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted applications.", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ @@ -103,9 +273,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -121,7 +291,83 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateApplicationRequest" + "type": "object", + "properties": { + "application": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "description": "The identifiers of the application. These are public and can be seen by any authenticated user in the network.", + "title": "The identifiers of the application. These are public and can be seen by any authenticated user in the network." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the application was created. This information is public and can be seen by any authenticated user in the network." + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the application was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the application was deleted. This information is public and can be seen by any authenticated user in the network." + }, + "name": { + "type": "string", + "description": "The name of the application." + }, + "description": { + "type": "string", + "description": "A description for the application." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this application. Typically used for organizing applications or for storing integration-specific data." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "dev_eui_counter": { + "type": "integer", + "format": "int64" + } + }, + "description": "Application is the message that defines an Application in the network." + }, + "field_mask": { + "type": "string", + "description": "The names of the application fields that should be updated." + } + } } } ], @@ -142,9 +388,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -156,15 +402,11 @@ "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", + "description": "The names of the application fields that should be returned.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ @@ -184,9 +426,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -197,6 +439,13 @@ "required": true, "type": "string" }, + { + "name": "order", + "description": "Order the results by this field path.\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, { "name": "limit", "description": "Limit the number of results per page.", @@ -229,9 +478,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -247,7 +496,25 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateApplicationAPIKeyRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "name": { + "type": "string" + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + } + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } } } ], @@ -268,9 +535,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -293,7 +560,48 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateApplicationAPIKeyRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "api_key": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Immutable and unique secret value of the API key.\nGenerated by the Access Server." + }, + "name": { + "type": "string", + "description": "User-defined (friendly) name for the API key." + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Rights that are granted to this API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the api key fields that should be updated." + } + } } } ], @@ -314,9 +622,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -352,9 +660,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -404,9 +712,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -456,9 +764,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -484,6 +792,13 @@ "required": false, "type": "integer", "format": "int64" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -497,13 +812,14 @@ "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -519,7 +835,15 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationCollaboratorRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "collaborator": { + "$ref": "#/definitions/v3Collaborator" + } + } } } ], @@ -528,8 +852,79 @@ ] } }, + "/applications/{application_ids.application_id}/collaborators/search": { + "get": { + "summary": "Search for accounts that match the conditions specified in the request.", + "operationId": "EntityRegistrySearch_SearchAccounts2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3SearchAccountsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "query", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "only_users", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "client_ids.client_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.gateway_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, "/applications/{application_ids.application_id}/devices": { "get": { + "summary": "List end devices in the given application.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", "operationId": "EndDeviceRegistry_List", "responses": { "200": { @@ -539,9 +934,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -553,15 +948,11 @@ "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { "name": "order", @@ -594,18 +985,21 @@ }, "/applications/{application_ids.application_id}/devices/{device_id}": { "delete": { + "summary": "Delete the end device with the given IDs.", + "description": "Before deleting an end device it first needs to be deleted from the\nNsEndDeviceRegistry, the AsEndDeviceRegistry and the JsEndDeviceRegistry.\nThis is NOT done automatically.", "operationId": "EndDeviceRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -628,7 +1022,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "join_eui", @@ -636,7 +1030,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "dev_addr", @@ -644,7 +1038,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" } ], "tags": [ @@ -660,13 +1054,110 @@ "200": { "description": "A successful response.", "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "ApplicationRegistry" + ] + } + }, + "/applications/{application_id}/dev-eui": { + "post": { + "summary": "Request DevEUI from the configured address block for a device inside the application.\nThe maximum number of DevEUI's issued per application can be configured.", + "operationId": "ApplicationRegistry_IssueDevEUI", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3IssueDevEUIResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "ApplicationRegistry" + ] + } + }, + "/applications/{application_id}/purge": { + "delete": { + "summary": "Purge the application. This will release the application ID for reuse.\nAll end devices must be deleted from the application before it can be deleted.\nThe application owner is responsible for clearing data from any (external) integrations\nthat may store and expose data by application ID", + "operationId": "ApplicationRegistry_Purge", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_id", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "ApplicationRegistry" + ] + } + }, + "/applications/{application_id}/restore": { + "post": { + "summary": "Restore a recently deleted application.", + "description": "Deployment configuration may specify if, and for how long after deletion,\nentities can be restored.", + "operationId": "ApplicationRegistry_Restore", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -695,9 +1186,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -716,6 +1207,8 @@ }, "/applications/{end_device.ids.application_ids.application_id}/devices": { "post": { + "summary": "Create a new end device within an application.", + "description": "After registering an end device, it also needs to be registered in\nthe NsEndDeviceRegistry that is exposed by the Network Server,\nthe AsEndDeviceRegistry that is exposed by the Application Server,\nand the JsEndDeviceRegistry that is exposed by the Join Server.", "operationId": "EndDeviceRegistry_Create", "responses": { "200": { @@ -725,9 +1218,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -743,7 +1236,270 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateEndDeviceRequest" + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "device_id": { + "type": "string" + }, + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + } + } } } ], @@ -754,6 +1510,7 @@ }, "/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { "put": { + "summary": "Update the end device, changing the fields specified by the field mask to the provided values.", "operationId": "EndDeviceRegistry_Update", "responses": { "200": { @@ -763,9 +1520,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -787,8 +1544,272 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateEndDeviceRequest" - } + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } + } } ], "tags": [ @@ -798,6 +1819,7 @@ }, "/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { "get": { + "summary": "Get the end device with the given identifiers, selecting the fields specified\nin the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", "operationId": "EndDeviceRegistry_Get", "responses": { "200": { @@ -807,9 +1829,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -832,7 +1854,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "end_device_ids.join_eui", @@ -840,7 +1862,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "end_device_ids.dev_addr", @@ -848,18 +1870,14 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ @@ -869,18 +1887,20 @@ }, "/as/applications/{application_ids.application_id}/devices/{device_id}": { "delete": { + "summary": "Delete deletes the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", "operationId": "AsEndDeviceRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -903,7 +1923,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "join_eui", @@ -911,7 +1931,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "dev_addr", @@ -919,7 +1939,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" } ], "tags": [ @@ -929,6 +1949,7 @@ }, "/as/applications/{application_ids.application_id}/devices/{device_id}/down": { "get": { + "summary": "List the items currently in the downlink queue.", "operationId": "AppAs_DownlinkQueueList", "responses": { "200": { @@ -938,9 +1959,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -963,7 +1984,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "join_eui", @@ -971,7 +1992,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "dev_addr", @@ -979,7 +2000,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" } ], "tags": [ @@ -989,6 +2010,7 @@ }, "/as/applications/{application_ids.application_id}/devices/{device_id}/packages": { "get": { + "summary": "List returns the available packages for the end device.", "operationId": "ApplicationPackageRegistry_List", "responses": { "200": { @@ -998,9 +2020,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1023,7 +2045,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "join_eui", @@ -1031,7 +2053,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "dev_addr", @@ -1039,7 +2061,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" } ], "tags": [ @@ -1049,7 +2071,7 @@ }, "/as/applications/{application_ids.application_id}/link": { "get": { - "summary": "Get returns the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "summary": "Get a link configuration from the Application Server to Network Server.\nThis only contains the configuration. Use GetLinkStats to view statistics and any link errors.", "operationId": "As_GetLink", "responses": { "200": { @@ -1059,9 +2081,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1073,15 +2095,10 @@ "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ @@ -1089,7 +2106,7 @@ ] }, "put": { - "summary": "Set creates or updates the device.", + "summary": "Set a link configuration from the Application Server a Network Server.\nThis call returns immediately after setting the link configuration; it does not wait for a link to establish.\nTo get link statistics or errors, use GetLinkStats.\nNote that there can only be one Application Server instance linked to a Network Server for a given application at a time.", "operationId": "As_SetLink", "responses": { "200": { @@ -1099,9 +2116,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1117,7 +2134,18 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationLinkRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "link": { + "$ref": "#/definitions/v3ApplicationLink" + }, + "field_mask": { + "type": "string" + } + } } } ], @@ -1128,18 +2156,20 @@ }, "/as/applications/{application_ids.application_id}/packages/associations/{f_port}": { "delete": { + "summary": "DeleteDefaultAssociation removes the default association on the FPort of the application.", "operationId": "ApplicationPackageRegistry_DeleteDefaultAssociation", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1163,21 +2193,251 @@ ] } }, + "/as/applications/{application_ids.application_id}/packages/storage/{type}": { + "get": { + "summary": "Returns a stream of application messages that have been stored in the database.", + "operationId": "ApplicationUpStorage_GetStoredApplicationUp2", + "responses": { + "200": { + "description": "A successful response.(streaming responses)", + "schema": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v3ApplicationUp" + }, + "error": { + "$ref": "#/definitions/googlerpcStatus" + } + }, + "title": "Stream result of v3ApplicationUp" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "type", + "description": "Query upstream messages of a specific type. If not set, then all upstream messages are returned.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "end_device_ids.device_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "limit", + "description": "Limit number of results.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "after", + "description": "Query upstream messages after this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "before", + "description": "Query upstream messages before this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "f_port", + "description": "Query uplinks on a specific FPort only.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order", + "description": "Order results.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the upstream message fields that should be returned. See the API reference\nfor allowed field names for each type of upstream message.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "last", + "description": "Query upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "ApplicationUpStorage" + ] + } + }, + "/as/applications/{application_ids.application_id}/packages/storage/{type}/count": { + "get": { + "summary": "Returns how many application messages have been stored in the database for an application or end device.", + "operationId": "ApplicationUpStorage_GetStoredApplicationUpCount2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetStoredApplicationUpCountResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "type", + "description": "Count upstream messages of a specific type. If not set, then all upstream messages are returned.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "end_device_ids.device_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "after", + "description": "Count upstream messages after this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "before", + "description": "Count upstream messages before this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "f_port", + "description": "Count uplinks on a specific FPort only.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "last", + "description": "Count upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "ApplicationUpStorage" + ] + } + }, "/as/applications/{application_id}/link": { "delete": { - "summary": "Delete deletes the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "summary": "Delete the link between the Application Server and Network Server for the specified application.", "operationId": "As_DeleteLink", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1196,6 +2456,7 @@ }, "/as/applications/{application_id}/link/stats": { "get": { + "summary": "GetLinkStats returns the link statistics.\nThis call returns a NotFound error code if there is no link for the given application identifiers.\nThis call returns the error code of the link error if linking to a Network Server failed.", "operationId": "As_GetLinkStats", "responses": { "200": { @@ -1205,9 +2466,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1226,6 +2487,7 @@ }, "/as/applications/{application_id}/mqtt-connection-info": { "get": { + "summary": "Get connection information to connect an MQTT client.", "operationId": "AppAs_GetMQTTConnectionInfo", "responses": { "200": { @@ -1235,9 +2497,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1256,6 +2518,7 @@ }, "/as/applications/{association.ids.end_device_ids.application_ids.application_id}/devices/{association.ids.end_device_ids.device_id}/packages/associations/{association.ids.f_port}": { "put": { + "summary": "SetAssociation updates or creates the association on the FPort of the end device.", "operationId": "ApplicationPackageRegistry_SetAssociation", "responses": { "200": { @@ -1265,9 +2528,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1296,7 +2559,62 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationPackageAssociationRequest" + "type": "object", + "properties": { + "association": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "package_name": { + "type": "string" + }, + "data": { + "type": "object" + } + } + }, + "field_mask": { + "type": "string" + } + } } } ], @@ -1307,6 +2625,7 @@ }, "/as/applications/{default.ids.application_ids.application_id}/packages/associations/{default.ids.f_port}": { "put": { + "summary": "SetDefaultAssociation updates or creates the default association on the FPort of the application.", "operationId": "ApplicationPackageRegistry_SetDefaultAssociation", "responses": { "200": { @@ -1316,9 +2635,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1341,7 +2660,39 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationPackageDefaultAssociationRequest" + "type": "object", + "properties": { + "default": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "package_name": { + "type": "string" + }, + "data": { + "type": "object" + } + } + }, + "field_mask": { + "type": "string" + } + } } } ], @@ -1352,6 +2703,7 @@ }, "/as/applications/{end_device.ids.application_ids.application_id}/devices": { "post": { + "summary": "Set creates or updates the device.", "operationId": "AsEndDeviceRegistry_Set2", "responses": { "200": { @@ -1361,9 +2713,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1379,7 +2731,274 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "device_id": { + "type": "string" + }, + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } } } ], @@ -1390,6 +3009,7 @@ }, "/as/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { "put": { + "summary": "Set creates or updates the device.", "operationId": "AsEndDeviceRegistry_Set", "responses": { "200": { @@ -1399,9 +3019,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1423,7 +3043,271 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } } } ], @@ -1434,6 +3318,7 @@ }, "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { "get": { + "summary": "Get returns the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", "operationId": "AsEndDeviceRegistry_Get", "responses": { "200": { @@ -1443,9 +3328,9 @@ } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1468,7 +3353,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "end_device_ids.join_eui", @@ -1476,7 +3361,7 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { "name": "end_device_ids.dev_addr", @@ -1484,18 +3369,14 @@ "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ @@ -1503,20 +3384,20 @@ ] } }, - "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/push": { + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/decode": { "post": { - "operationId": "AppAs_DownlinkQueuePush", + "operationId": "AppAs_DecodeDownlink", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3DecodeDownlinkResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1538,29 +3419,69 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3DownlinkQueueRequest" - } - } - ], - "tags": [ - "AppAs" - ] + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers" + }, + "downlink": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "formatter": { + "$ref": "#/definitions/v3PayloadFormatter" + }, + "parameter": { + "type": "string" + } + } + } + } + ], + "tags": [ + "AppAs" + ] } }, - "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/replace": { + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/encode": { "post": { - "operationId": "AppAs_DownlinkQueueReplace", + "operationId": "AppAs_EncodeDownlink", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EncodeDownlinkResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1582,7 +3503,47 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3DownlinkQueueRequest" + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers" + }, + "downlink": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "formatter": { + "$ref": "#/definitions/v3PayloadFormatter" + }, + "parameter": { + "type": "string" + } + } } } ], @@ -1591,20 +3552,22 @@ ] } }, - "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/associations/{f_port}": { - "delete": { - "operationId": "ApplicationPackageRegistry_DeleteAssociation", + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/push": { + "post": { + "summary": "Push downlink messages to the end of the downlink queue.", + "operationId": "AppAs_DownlinkQueuePush", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1622,56 +3585,69 @@ "type": "string" }, { - "name": "f_port", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "integer", - "format": "int64" - }, - { - "name": "end_device_ids.dev_eui", - "description": "The LoRaWAN DevEUI.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "end_device_ids.join_eui", - "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "end_device_ids.dev_addr", - "description": "The LoRaWAN DevAddr.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" + "schema": { + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + } + } + } + } } ], "tags": [ - "ApplicationPackageRegistry" + "AppAs" ] } }, - "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/simulate": { + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/replace": { "post": { - "operationId": "AppAs_SimulateUplink", + "summary": "Replace the entire downlink queue with the specified messages.\nThis can also be used to empty the queue by specifying no messages.", + "operationId": "AppAs_DownlinkQueueReplace", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -1693,7 +3669,41 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3ApplicationUp" + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + } + } + } } } ], @@ -1702,86 +3712,68 @@ ] } }, - "/as/applications/{ids.application_ids.application_id}/devices/{ids.device_id}/packages/associations": { - "get": { - "operationId": "ApplicationPackageRegistry_ListAssociations", + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/associations/{f_port}": { + "delete": { + "summary": "DeleteAssociation removes the association on the FPort of the end device.", + "operationId": "ApplicationPackageRegistry_DeleteAssociation", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPackageAssociations" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "ids.application_ids.application_id", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "ids.device_id", + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "ids.dev_eui", + "name": "f_port", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "end_device_ids.dev_eui", "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "ids.join_eui", + "name": "end_device_ids.join_eui", "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "ids.dev_addr", + "name": "end_device_ids.dev_addr", "description": "The LoRaWAN DevAddr.", "in": "query", "required": false, "type": "string", - "format": "byte" - }, - { - "name": "limit", - "description": "Limit the number of results per page.\nEach page is ordered by the FPort.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "format": "string" } ], "tags": [ @@ -1789,302 +3781,457 @@ ] } }, - "/as/applications/{ids.application_ids.application_id}/packages/associations/{ids.f_port}": { + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}": { "get": { - "operationId": "ApplicationPackageRegistry_GetDefaultAssociation", + "summary": "Returns a stream of application messages that have been stored in the database.", + "operationId": "ApplicationUpStorage_GetStoredApplicationUp", "responses": { "200": { - "description": "A successful response.", + "description": "A successful response.(streaming responses)", "schema": { - "$ref": "#/definitions/v3ApplicationPackageDefaultAssociation" + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v3ApplicationUp" + }, + "error": { + "$ref": "#/definitions/googlerpcStatus" + } + }, + "title": "Stream result of v3ApplicationUp" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "ids.application_ids.application_id", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "ids.f_port", + "name": "end_device_ids.device_id", "in": "path", "required": true, - "type": "integer", - "format": "int64" + "type": "string" + }, + { + "name": "type", + "description": "Query upstream messages of a specific type. If not set, then all upstream messages are returned.", + "in": "path", + "required": true, + "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - } - ], - "tags": [ - "ApplicationPackageRegistry" - ] - } - }, - "/as/applications/{ids.application_id}/packages/associations": { - "get": { - "operationId": "ApplicationPackageRegistry_ListDefaultAssociations", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3ApplicationPackageDefaultAssociations" - } + "type": "string", + "format": "string" }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "ids.application_id", - "in": "path", - "required": true, - "type": "string" + "name": "end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" }, { "name": "limit", - "description": "Limit the number of results per page.\nEach page is ordered by the FPort.", + "description": "Limit number of results.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "after", + "description": "Query upstream messages after this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "before", + "description": "Query upstream messages before this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "f_port", + "description": "Query uplinks on a specific FPort only.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "order", + "description": "Order results.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the upstream message fields that should be returned. See the API reference\nfor allowed field names for each type of upstream message.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "last", + "description": "Query upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ApplicationPackageRegistry" + "ApplicationUpStorage" ] } }, - "/as/applications/{ids.end_device_ids.application_ids.application_id}/devices/{ids.end_device_ids.device_id}/packages/associations/{ids.f_port}": { + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}/count": { "get": { - "operationId": "ApplicationPackageRegistry_GetAssociation", + "summary": "Returns how many application messages have been stored in the database for an application or end device.", + "operationId": "ApplicationUpStorage_GetStoredApplicationUpCount", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPackageAssociation" + "$ref": "#/definitions/v3GetStoredApplicationUpCountResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "ids.end_device_ids.application_ids.application_id", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "ids.end_device_ids.device_id", + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "ids.f_port", + "name": "type", + "description": "Count upstream messages of a specific type. If not set, then all upstream messages are returned.", "in": "path", "required": true, - "type": "integer", - "format": "int64" + "type": "string" }, { - "name": "ids.end_device_ids.dev_eui", + "name": "end_device_ids.dev_eui", "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "ids.end_device_ids.join_eui", + "name": "end_device_ids.join_eui", "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "ids.end_device_ids.dev_addr", + "name": "end_device_ids.dev_addr", "description": "The LoRaWAN DevAddr.", "in": "query", "required": false, "type": "string", - "format": "byte" + "format": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "after", + "description": "Count upstream messages after this timestamp only. Cannot be used in conjunction with last.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - } - ], - "tags": [ - "ApplicationPackageRegistry" - ] - } - }, - "/as/pubsub-formats": { - "get": { - "operationId": "ApplicationPubSubRegistry_GetFormats", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3ApplicationPubSubFormats" - } + "type": "string", + "format": "date-time" }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } + { + "name": "before", + "description": "Count upstream messages before this timestamp only. Cannot be used in conjunction with last.", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "f_port", + "description": "Count uplinks on a specific FPort only.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "last", + "description": "Count upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before.", + "in": "query", + "required": false, + "type": "string" } - }, + ], "tags": [ - "ApplicationPubSubRegistry" + "ApplicationUpStorage" ] } }, - "/as/pubsub/{application_ids.application_id}": { - "get": { - "operationId": "ApplicationPubSubRegistry_List", + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/decode": { + "post": { + "operationId": "AppAs_DecodeUplink", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPubSubs" + "$ref": "#/definitions/v3DecodeUplinkResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "application_ids.application_id", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "name": "end_device_ids.device_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers" + }, + "uplink": { + "$ref": "#/definitions/v3ApplicationUplink" + }, + "formatter": { + "$ref": "#/definitions/v3PayloadFormatter" + }, + "parameter": { + "type": "string" + } + } + } } ], "tags": [ - "ApplicationPubSubRegistry" + "AppAs" ] } }, - "/as/pubsub/{application_ids.application_id}/{pub_sub_id}": { - "delete": { - "operationId": "ApplicationPubSubRegistry_Delete", + "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/simulate": { + "post": { + "summary": "Simulate an upstream message. This can be used to test integrations.", + "operationId": "AppAs_SimulateUplink", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "application_ids.application_id", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "pub_sub_id", + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "correlation_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "received_at": { + "type": "string", + "format": "date-time", + "description": "Server time when the Application Server received the message." + }, + "uplink_message": { + "$ref": "#/definitions/v3ApplicationUplink" + }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationUplinkNormalized" + }, + "join_accept": { + "$ref": "#/definitions/v3ApplicationJoinAccept" + }, + "downlink_ack": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "downlink_nack": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "downlink_sent": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "downlink_failed": { + "$ref": "#/definitions/v3ApplicationDownlinkFailed" + }, + "downlink_queued": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationInvalidatedDownlinks" + }, + "location_solved": { + "$ref": "#/definitions/v3ApplicationLocation" + }, + "service_data": { + "$ref": "#/definitions/v3ApplicationServiceData" + }, + "simulated": { + "type": "boolean", + "description": "Signals if the message is coming from the Network Server or is simulated." + } + }, + "description": "Application uplink message." + } } ], "tags": [ - "ApplicationPubSubRegistry" + "AppAs" ] } }, - "/as/pubsub/{ids.application_ids.application_id}/{ids.pub_sub_id}": { + "/as/applications/{ids.application_ids.application_id}/devices/{ids.device_id}/packages/associations": { "get": { - "operationId": "ApplicationPubSubRegistry_Get", + "summary": "ListAssociations returns all of the associations of the end device.", + "operationId": "ApplicationPackageRegistry_ListAssociations", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPubSub" + "$ref": "#/definitions/v3ApplicationPackageAssociations" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -2096,222 +4243,292 @@ "type": "string" }, { - "name": "ids.pub_sub_id", + "name": "ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "ids.dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string", + "format": "string" + }, + { + "name": "ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.\nEach page is ordered by the FPort.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ApplicationPubSubRegistry" + "ApplicationPackageRegistry" ] } }, - "/as/pubsub/{pubsub.ids.application_ids.application_id}": { - "post": { - "operationId": "ApplicationPubSubRegistry_Set2", + "/as/applications/{ids.application_ids.application_id}/packages/associations/{ids.f_port}": { + "get": { + "summary": "GetDefaultAssociation returns the default association registered on the FPort of the application.", + "operationId": "ApplicationPackageRegistry_GetDefaultAssociation", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPubSub" + "$ref": "#/definitions/v3ApplicationPackageDefaultAssociation" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "pubsub.ids.application_ids.application_id", + "name": "ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "ids.f_port", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3SetApplicationPubSubRequest" - } + "type": "integer", + "format": "int64" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ApplicationPubSubRegistry" + "ApplicationPackageRegistry" ] } }, - "/as/pubsub/{pubsub.ids.application_ids.application_id}/{pubsub.ids.pub_sub_id}": { - "put": { - "operationId": "ApplicationPubSubRegistry_Set", + "/as/applications/{ids.application_id}/packages/associations": { + "get": { + "summary": "ListDefaultAssociations returns all of the default associations of the application.", + "operationId": "ApplicationPackageRegistry_ListDefaultAssociations", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationPubSub" + "$ref": "#/definitions/v3ApplicationPackageDefaultAssociations" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "pubsub.ids.application_ids.application_id", + "name": "ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "pubsub.ids.pub_sub_id", - "in": "path", - "required": true, - "type": "string" + "name": "limit", + "description": "Limit the number of results per page.\nEach page is ordered by the FPort.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3SetApplicationPubSubRequest" - } - } - ], - "tags": [ - "ApplicationPubSubRegistry" - ] - } - }, - "/as/webhook-formats": { - "get": { - "operationId": "ApplicationWebhookRegistry_GetFormats", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3ApplicationWebhookFormats" - } + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" } - }, + ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPackageRegistry" ] } }, - "/as/webhook-templates": { + "/as/applications/{ids.end_device_ids.application_ids.application_id}/devices/{ids.end_device_ids.device_id}/packages/associations/{ids.f_port}": { "get": { - "operationId": "ApplicationWebhookRegistry_ListTemplates", + "summary": "GetAssociation returns the association registered on the FPort of the end device.", + "operationId": "ApplicationPackageRegistry_GetAssociation", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhookTemplates" + "$ref": "#/definitions/v3ApplicationPackageAssociation" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - } + "name": "ids.end_device_ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "ids.end_device_ids.device_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "ids.f_port", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "ids.end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "ids.end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "ids.end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPackageRegistry" ] } }, - "/as/webhook-templates/{ids.template_id}": { + "/as/configuration": { "get": { - "operationId": "ApplicationWebhookRegistry_GetTemplate", + "operationId": "As_GetConfiguration", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhookTemplate" + "$ref": "#/definitions/v3GetAsConfigurationResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "ids.template_id", - "in": "path", - "required": true, - "type": "string" + "tags": [ + "As" + ] + } + }, + "/as/pubsub-formats": { + "get": { + "operationId": "ApplicationPubSubRegistry_GetFormats", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ApplicationPubSubFormats" + } }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } } - ], + }, "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/as/webhooks/{application_ids.application_id}": { + "/as/pubsub/{application_ids.application_id}": { "get": { - "operationId": "ApplicationWebhookRegistry_List", + "operationId": "ApplicationPubSubRegistry_List", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhooks" + "$ref": "#/definitions/v3ApplicationPubSubs" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -2323,36 +4540,32 @@ "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/as/webhooks/{application_ids.application_id}/{webhook_id}": { + "/as/pubsub/{application_ids.application_id}/{pub_sub_id}": { "delete": { - "operationId": "ApplicationWebhookRegistry_Delete", + "operationId": "ApplicationPubSubRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -2364,31 +4577,31 @@ "type": "string" }, { - "name": "webhook_id", + "name": "pub_sub_id", "in": "path", "required": true, "type": "string" } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/as/webhooks/{ids.application_ids.application_id}/{ids.webhook_id}": { + "/as/pubsub/{ids.application_ids.application_id}/{ids.pub_sub_id}": { "get": { - "operationId": "ApplicationWebhookRegistry_Get", + "operationId": "ApplicationPubSubRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhook" + "$ref": "#/definitions/v3ApplicationPubSub" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -2400,48 +4613,43 @@ "type": "string" }, { - "name": "ids.webhook_id", + "name": "ids.pub_sub_id", "in": "path", "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/as/webhooks/{webhook.ids.application_ids.application_id}": { + "/as/pubsub/{pubsub.ids.application_ids.application_id}": { "post": { - "operationId": "ApplicationWebhookRegistry_Set2", + "operationId": "ApplicationPubSubRegistry_Set2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhook" + "$ref": "#/definitions/v3ApplicationPubSub" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "webhook.ids.application_ids.application_id", + "name": "pubsub.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" @@ -2451,41 +4659,128 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationWebhookRequest" + "type": "object", + "properties": { + "pubsub": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "pub_sub_id": { + "type": "string" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "format": { + "type": "string", + "description": "The format to use for the body.\nSupported values depend on the Application Server configuration." + }, + "nats": { + "$ref": "#/definitions/ApplicationPubSubNATSProvider" + }, + "mqtt": { + "$ref": "#/definitions/ApplicationPubSubMQTTProvider" + }, + "aws_iot": { + "$ref": "#/definitions/ApplicationPubSubAWSIoTProvider" + }, + "base_topic": { + "type": "string", + "description": "Base topic name to which the messages topic is appended." + }, + "downlink_push": { + "$ref": "#/definitions/v3ApplicationPubSubMessage", + "description": "The topic to which the Application Server subscribes for downlink queue push operations." + }, + "downlink_replace": { + "$ref": "#/definitions/v3ApplicationPubSubMessage", + "description": "The topic to which the Application Server subscribes for downlink queue replace operations." + }, + "uplink_message": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "join_accept": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_ack": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_nack": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_sent": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_failed": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_queued": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "location_solved": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "service_data": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + } + } + }, + "field_mask": { + "type": "string" + } + } } } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/as/webhooks/{webhook.ids.application_ids.application_id}/{webhook.ids.webhook_id}": { + "/as/pubsub/{pubsub.ids.application_ids.application_id}/{pubsub.ids.pub_sub_id}": { "put": { - "operationId": "ApplicationWebhookRegistry_Set", + "operationId": "ApplicationPubSubRegistry_Set", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3ApplicationWebhook" + "$ref": "#/definitions/v3ApplicationPubSub" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "webhook.ids.application_ids.application_id", + "name": "pubsub.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "webhook.ids.webhook_id", + "name": "pubsub.ids.pub_sub_id", "in": "path", "required": true, "type": "string" @@ -2495,361 +4790,464 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetApplicationWebhookRequest" + "type": "object", + "properties": { + "pubsub": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "format": { + "type": "string", + "description": "The format to use for the body.\nSupported values depend on the Application Server configuration." + }, + "nats": { + "$ref": "#/definitions/ApplicationPubSubNATSProvider" + }, + "mqtt": { + "$ref": "#/definitions/ApplicationPubSubMQTTProvider" + }, + "aws_iot": { + "$ref": "#/definitions/ApplicationPubSubAWSIoTProvider" + }, + "base_topic": { + "type": "string", + "description": "Base topic name to which the messages topic is appended." + }, + "downlink_push": { + "$ref": "#/definitions/v3ApplicationPubSubMessage", + "description": "The topic to which the Application Server subscribes for downlink queue push operations." + }, + "downlink_replace": { + "$ref": "#/definitions/v3ApplicationPubSubMessage", + "description": "The topic to which the Application Server subscribes for downlink queue replace operations." + }, + "uplink_message": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "join_accept": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_ack": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_nack": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_sent": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_failed": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_queued": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "location_solved": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, + "service_data": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + } + } + }, + "field_mask": { + "type": "string" + } + } } } ], "tags": [ - "ApplicationWebhookRegistry" + "ApplicationPubSubRegistry" ] } }, - "/auth_info": { + "/as/webhook-formats": { "get": { - "operationId": "EntityAccess_AuthInfo", + "operationId": "ApplicationWebhookRegistry_GetFormats", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3AuthInfoResponse" + "$ref": "#/definitions/v3ApplicationWebhookFormats" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "tags": [ - "EntityAccess" + "ApplicationWebhookRegistry" ] } }, - "/clients": { + "/as/webhook-templates": { "get": { - "operationId": "ClientRegistry_List", + "operationId": "ApplicationWebhookRegistry_ListTemplates", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Clients" + "$ref": "#/definitions/v3ApplicationWebhookTemplates" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "field_mask", "in": "query", "required": false, "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" } ], "tags": [ - "ClientRegistry" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client.ids.client_id}": { - "put": { - "operationId": "ClientRegistry_Update", + "/as/webhook-templates/{ids.template_id}": { + "get": { + "operationId": "ApplicationWebhookRegistry_GetTemplate", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Client" + "$ref": "#/definitions/v3ApplicationWebhookTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client.ids.client_id", + "name": "ids.template_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3UpdateClientRequest" - } + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ClientRegistry" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client_ids.client_id}": { + "/as/webhooks/{application_ids.application_id}": { "get": { - "operationId": "ClientRegistry_Get", + "operationId": "ApplicationWebhookRegistry_List", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Client" + "$ref": "#/definitions/v3ApplicationWebhooks" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_ids.client_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "field_mask", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" } ], "tags": [ - "ClientRegistry" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client_ids.client_id}/collaborator/organization/{collaborator.organization_ids.organization_id}": { - "get": { - "operationId": "ClientAccess_GetCollaborator2", + "/as/webhooks/{application_ids.application_id}/{webhook_id}": { + "delete": { + "operationId": "ApplicationWebhookRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetCollaboratorResponse" - } + "type": "object", + "properties": {} + } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_ids.client_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "webhook_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" } ], "tags": [ - "ClientAccess" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client_ids.client_id}/collaborator/user/{collaborator.user_ids.user_id}": { + "/as/webhooks/{ids.application_ids.application_id}/{ids.webhook_id}": { "get": { - "operationId": "ClientAccess_GetCollaborator", + "operationId": "ApplicationWebhookRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetCollaboratorResponse" + "$ref": "#/definitions/v3ApplicationWebhook" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_ids.client_id", + "name": "ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "ids.webhook_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "field_mask", "in": "query", "required": false, "type": "string" } ], "tags": [ - "ClientAccess" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client_ids.client_id}/collaborators": { - "get": { - "operationId": "ClientAccess_ListCollaborators", + "/as/webhooks/{webhook.ids.application_ids.application_id}": { + "post": { + "operationId": "ApplicationWebhookRegistry_Set2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Collaborators" + "$ref": "#/definitions/v3ApplicationWebhook" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_ids.client_id", + "name": "webhook.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "webhook": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "webhook_id": { + "type": "string" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "base_url": { + "type": "string", + "description": "Base URL to which the message's path is appended." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP headers to use." + }, + "format": { + "type": "string", + "description": "The format to use for the body.\nSupported values depend on the Application Server configuration." + }, + "template_ids": { + "$ref": "#/definitions/v3ApplicationWebhookTemplateIdentifiers", + "description": "The ID of the template that was used to create the Webhook." + }, + "template_fields": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "The value of the fields used by the template. Maps field.id to the value." + }, + "downlink_api_key": { + "type": "string", + "description": "The API key to be used for downlink queue operations.\nThe field is provided for convenience reasons, and can contain API keys with additional rights (albeit this is discouraged)." + }, + "uplink_message": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "join_accept": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_ack": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_nack": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_sent": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_failed": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_queued": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "location_solved": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "service_data": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "health_status": { + "$ref": "#/definitions/v3ApplicationWebhookHealth" + }, + "field_mask": { + "type": "string" + } + } + }, + "field_mask": { + "type": "string" + } + } + } } ], "tags": [ - "ClientAccess" + "ApplicationWebhookRegistry" ] - }, + } + }, + "/as/webhooks/{webhook.ids.application_ids.application_id}/{webhook.ids.webhook_id}": { "put": { - "operationId": "ClientAccess_SetCollaborator", + "operationId": "ApplicationWebhookRegistry_Set", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3ApplicationWebhook" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_ids.client_id", + "name": "webhook.ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "webhook.ids.webhook_id", "in": "path", "required": true, "type": "string" @@ -2859,1231 +5257,1539 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetClientCollaboratorRequest" + "type": "object", + "properties": { + "webhook": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "base_url": { + "type": "string", + "description": "Base URL to which the message's path is appended." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP headers to use." + }, + "format": { + "type": "string", + "description": "The format to use for the body.\nSupported values depend on the Application Server configuration." + }, + "template_ids": { + "$ref": "#/definitions/v3ApplicationWebhookTemplateIdentifiers", + "description": "The ID of the template that was used to create the Webhook." + }, + "template_fields": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "The value of the fields used by the template. Maps field.id to the value." + }, + "downlink_api_key": { + "type": "string", + "description": "The API key to be used for downlink queue operations.\nThe field is provided for convenience reasons, and can contain API keys with additional rights (albeit this is discouraged)." + }, + "uplink_message": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "join_accept": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_ack": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_nack": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_sent": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_failed": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_queued": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "location_solved": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "service_data": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "health_status": { + "$ref": "#/definitions/v3ApplicationWebhookHealth" + }, + "field_mask": { + "type": "string" + } + } + }, + "field_mask": { + "type": "string" + } + } } } ], "tags": [ - "ClientAccess" + "ApplicationWebhookRegistry" ] } }, - "/clients/{client_id}": { - "delete": { - "operationId": "ClientRegistry_Delete", + "/auth_info": { + "get": { + "summary": "AuthInfo returns information about the authentication that is used on the request.", + "operationId": "EntityAccess_AuthInfo", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3AuthInfoResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "client_id", - "in": "path", - "required": true, - "type": "string" - } - ], "tags": [ - "ClientRegistry" + "EntityAccess" ] } }, - "/clients/{client_id}/rights": { + "/clients": { "get": { - "operationId": "ClientAccess_ListRights", + "summary": "List OAuth clients where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the OAuth clients the caller\nhas access to.\nSimilar to Get, this selects the fields specified in the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ClientRegistry_List", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Rights" + "$ref": "#/definitions/v3Clients" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "client_id", - "in": "path", - "required": true, + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, "type": "string" - } - ], - "tags": [ - "ClientAccess" - ] - } - }, - "/configuration/frequency-plans": { - "get": { - "operationId": "Configuration_ListFrequencyPlans", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3ListFrequencyPlansResponse" - } }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "base_frequency", - "description": "Optional base frequency in MHz for hardware support (433, 470, 868 or 915).", + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the client fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", "in": "query", "required": false, "type": "integer", "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted clients.", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ - "Configuration" + "ClientRegistry" ] } }, - "/contact_info/validation": { - "post": { - "operationId": "ContactInfoRegistry_RequestValidation", + "/clients/{client.ids.client_id}": { + "put": { + "summary": "Update the OAuth client, changing the fields specified by the field mask to the provided values.", + "operationId": "ClientRegistry_Update", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/lorawanv3ContactInfoValidation" + "$ref": "#/definitions/v3Client" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "tags": [ - "ContactInfoRegistry" - ] - }, - "patch": { - "operationId": "ContactInfoRegistry_Validate", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "properties": {} - } + "parameters": [ + { + "name": "client.ids.client_id", + "in": "path", + "required": true, + "type": "string" }, - "default": { - "description": "An unexpected error response", + { + "name": "body", + "in": "body", + "required": true, "schema": { - "$ref": "#/definitions/runtimeError" + "type": "object", + "properties": { + "client": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "description": "The identifiers of the OAuth client. These are public and can be seen by any authenticated user in the network.", + "title": "The identifiers of the OAuth client. These are public and can be seen by any authenticated user in the network." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the OAuth client was created. This information is public and can be seen by any authenticated user in the network." + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the OAuth client was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the OAuth client was deleted. This information is public and can be seen by any authenticated user in the network." + }, + "name": { + "type": "string", + "description": "The name of the OAuth client. This information is public and can be seen by any authenticated user in the network." + }, + "description": { + "type": "string", + "description": "A description for the OAuth client. This information is public and can be seen by any authenticated user in the network." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this client. Typically used for organizing clients or for storing integration-specific data." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application.\nThis information is public and can be seen by any authenticated user in the network.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "secret": { + "type": "string", + "description": "The client secret is only visible to collaborators of the client." + }, + "redirect_uris": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The allowed redirect URIs against which authorization requests are checked.\nIf the authorization request does not pass a redirect URI, the first one\nfrom this list is taken.\nThis information is public and can be seen by any authenticated user in the network." + }, + "logout_redirect_uris": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The allowed logout redirect URIs against which client initiated logout\nrequests are checked. If the authorization request does not pass a redirect\nURI, the first one from this list is taken.\nThis information is public and can be seen by any authenticated user in the network." + }, + "state": { + "$ref": "#/definitions/v3State", + "description": "The reviewing state of the client.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins.\nIf state_description is not updated when updating state, state_description is cleared." + }, + "state_description": { + "type": "string", + "description": "A description for the state field.\nThis field can only be modified by admins, and should typically only be updated\nwhen also updating `state`." + }, + "skip_authorization": { + "type": "boolean", + "description": "If set, the authorization page will be skipped.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." + }, + "endorsed": { + "type": "boolean", + "description": "If set, the authorization page will show endorsement.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." + }, + "grants": { + "type": "array", + "items": { + "$ref": "#/definitions/v3GrantType" + }, + "description": "OAuth flows that can be used for the client to get a token.\nThis information is public and can be seen by any authenticated user in the network.\nAfter a client is created, this field can only be modified by admins." + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Rights denotes what rights the client will have access to.\nThis information is public and can be seen by any authenticated user in the network.\nUsers that previously authorized this client will have to re-authorize the\nclient after rights are added to this list." + } + }, + "description": "An OAuth client on the network." + }, + "field_mask": { + "type": "string", + "description": "The names of the client fields that should be updated." + } + } } } - }, + ], "tags": [ - "ContactInfoRegistry" + "ClientRegistry" ] } }, - "/edcs/applications/{application_ids.application_id}/authorize": { - "post": { - "operationId": "EndDeviceClaimingServer_AuthorizeApplication", + "/clients/{client_ids.client_id}": { + "get": { + "summary": "Get the OAuth client with the given identifiers, selecting the fields specified\nin the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ClientRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3Client" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "application_ids.application_id", + "name": "client_ids.client_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3AuthorizeApplicationRequest" - } + "name": "field_mask", + "description": "The names of the client fields that should be returned.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "EndDeviceClaimingServer" + "ClientRegistry" ] } }, - "/edcs/applications/{application_id}/authorize": { - "delete": { - "operationId": "EndDeviceClaimingServer_UnauthorizeApplication", + "/clients/{client_ids.client_id}/collaborator/organization/{collaborator.organization_ids.organization_id}": { + "get": { + "summary": "Get the rights of a collaborator (member) of the client.\nPseudo-rights in the response (such as the \"_ALL\" right) are not expanded.", + "operationId": "ClientAccess_GetCollaborator2", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3GetCollaboratorResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "application_id", + "name": "client_ids.client_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", "in": "path", "required": true, "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "EndDeviceClaimingServer" + "ClientAccess" ] } }, - "/edcs/claim": { - "post": { - "operationId": "EndDeviceClaimingServer_Claim", + "/clients/{client_ids.client_id}/collaborator/user/{collaborator.user_ids.user_id}": { + "get": { + "summary": "Get the rights of a collaborator (member) of the client.\nPseudo-rights in the response (such as the \"_ALL\" right) are not expanded.", + "operationId": "ClientAccess_GetCollaborator", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDeviceIdentifiers" + "$ref": "#/definitions/v3GetCollaboratorResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "body", - "in": "body", + "name": "client_ids.client_id", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3ClaimEndDeviceRequest" - } + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "EndDeviceClaimingServer" + "ClientAccess" ] } }, - "/edtc/convert": { - "post": { - "operationId": "EndDeviceTemplateConverter_Convert", + "/clients/{client_ids.client_id}/collaborators": { + "get": { + "summary": "List the collaborators on this OAuth client.", + "operationId": "ClientAccess_ListCollaborators", "responses": { "200": { - "description": "A successful response.(streaming responses)", + "description": "A successful response.", "schema": { - "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/v3EndDeviceTemplate" - }, - "error": { - "$ref": "#/definitions/runtimeStreamError" - } - }, - "title": "Stream result of v3EndDeviceTemplate" + "$ref": "#/definitions/v3Collaborators" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "body", - "in": "body", + "name": "client_ids.client_id", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3ConvertEndDeviceTemplateRequest" - } + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "EndDeviceTemplateConverter" + "ClientAccess" ] - } - }, - "/edtc/formats": { - "get": { - "operationId": "EndDeviceTemplateConverter_ListFormats", + }, + "put": { + "summary": "Set the rights of a collaborator (member) on the OAuth client.\nThis method can also be used to delete the collaborator, by giving them no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "ClientAccess_SetCollaborator", "responses": { "200": { "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3EndDeviceTemplateFormats" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "tags": [ - "EndDeviceTemplateConverter" - ] - } - }, - "/events": { - "post": { - "operationId": "Events_Stream", - "responses": { - "200": { - "description": "A successful response.(streaming responses)", "schema": { "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/v3Event" - }, - "error": { - "$ref": "#/definitions/runtimeStreamError" - } - }, - "title": "Stream result of v3Event" + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ + { + "name": "client_ids.client_id", + "in": "path", + "required": true, + "type": "string" + }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3StreamEventsRequest" + "type": "object", + "properties": { + "client_ids": { + "type": "object" + }, + "collaborator": { + "$ref": "#/definitions/v3Collaborator" + } + } } } ], "tags": [ - "Events" + "ClientAccess" ] } }, - "/gateways": { + "/clients/{client_ids.client_id}/collaborators/search": { "get": { - "operationId": "GatewayRegistry_List", + "summary": "Search for accounts that match the conditions specified in the request.", + "operationId": "EntityRegistrySearch_SearchAccounts3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateways" + "$ref": "#/definitions/v3SearchAccountsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, + "name": "client_ids.client_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "query", "in": "query", "required": false, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "only_users", "in": "query", "required": false, - "type": "string" + "type": "boolean" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "application_ids.application_id", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "gateway_ids.gateway_id", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string" } ], "tags": [ - "GatewayRegistry" + "EntityRegistrySearch" ] } }, - "/gateways/{gateway.ids.gateway_id}": { - "put": { - "operationId": "GatewayRegistry_Update", + "/clients/{client_id}": { + "delete": { + "summary": "Delete the OAuth client. This may not release the client ID for reuse.", + "operationId": "ClientRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateway" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway.ids.gateway_id", + "name": "client_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3UpdateGatewayRequest" - } } ], "tags": [ - "GatewayRegistry" + "ClientRegistry" ] } }, - "/gateways/{gateway_ids.gateway_id}": { - "get": { - "operationId": "GatewayRegistry_Get", + "/clients/{client_id}/purge": { + "delete": { + "summary": "Purge the client. This will release the client ID for reuse.", + "operationId": "ClientRegistry_Purge", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateway" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", + "name": "client_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" } ], "tags": [ - "GatewayRegistry" + "ClientRegistry" ] } }, - "/gateways/{gateway_ids.gateway_id}/api-keys": { - "get": { - "operationId": "GatewayAccess_ListAPIKeys", + "/clients/{client_id}/restore": { + "post": { + "summary": "Restore a recently deleted client.", + "description": "Deployment configuration may specify if, and for how long after deletion,\nentities can be restored.", + "operationId": "ClientRegistry_Restore", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKeys" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", + "name": "client_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" } ], "tags": [ - "GatewayAccess" + "ClientRegistry" ] - }, - "post": { - "operationId": "GatewayAccess_CreateAPIKey", + } + }, + "/clients/{client_id}/rights": { + "get": { + "summary": "List the rights the caller has on this application.", + "operationId": "ClientAccess_ListRights", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "$ref": "#/definitions/v3Rights" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", + "name": "client_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3CreateGatewayAPIKeyRequest" - } } ], "tags": [ - "GatewayAccess" + "ClientAccess" ] } }, - "/gateways/{gateway_ids.gateway_id}/api-keys/{api_key.id}": { - "put": { - "operationId": "GatewayAccess_UpdateAPIKey", + "/configuration/bands": { + "get": { + "operationId": "Configuration_ListBands", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "$ref": "#/definitions/v3ListBandsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "api_key.id", - "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", - "in": "path", - "required": true, + "name": "band_id", + "description": "Optional Band ID to filter the results.\nIf unused, all supported Bands are returned.", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3UpdateGatewayAPIKeyRequest" - } + "name": "phy_version", + "description": "Optional PHY version to filter the results.\nIf unused, all supported versions are returned.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "PHY_UNKNOWN", + "PHY_V1_0", + "TS001_V1_0", + "PHY_V1_0_1", + "TS001_V1_0_1", + "PHY_V1_0_2_REV_A", + "RP001_V1_0_2", + "PHY_V1_0_2_REV_B", + "RP001_V1_0_2_REV_B", + "PHY_V1_1_REV_A", + "RP001_V1_1_REV_A", + "PHY_V1_1_REV_B", + "RP001_V1_1_REV_B", + "PHY_V1_0_3_REV_A", + "RP001_V1_0_3_REV_A", + "RP002_V1_0_0", + "RP002_V1_0_1", + "RP002_V1_0_2", + "RP002_V1_0_3" + ], + "default": "PHY_UNKNOWN" } ], "tags": [ - "GatewayAccess" + "Configuration" ] } }, - "/gateways/{gateway_ids.gateway_id}/api-keys/{key_id}": { + "/configuration/bands/{band_id}": { "get": { - "operationId": "GatewayAccess_GetAPIKey", + "operationId": "Configuration_ListBands2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "$ref": "#/definitions/v3ListBandsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "key_id", - "description": "Unique public identifier for the API key.", + "name": "band_id", + "description": "Optional Band ID to filter the results.\nIf unused, all supported Bands are returned.", "in": "path", "required": true, "type": "string" }, { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "phy_version", + "description": "Optional PHY version to filter the results.\nIf unused, all supported versions are returned.", "in": "query", "required": false, "type": "string", - "format": "byte" + "enum": [ + "PHY_UNKNOWN", + "PHY_V1_0", + "TS001_V1_0", + "PHY_V1_0_1", + "TS001_V1_0_1", + "PHY_V1_0_2_REV_A", + "RP001_V1_0_2", + "PHY_V1_0_2_REV_B", + "RP001_V1_0_2_REV_B", + "PHY_V1_1_REV_A", + "RP001_V1_1_REV_A", + "PHY_V1_1_REV_B", + "RP001_V1_1_REV_B", + "PHY_V1_0_3_REV_A", + "RP001_V1_0_3_REV_A", + "RP002_V1_0_0", + "RP002_V1_0_1", + "RP002_V1_0_2", + "RP002_V1_0_3" + ], + "default": "PHY_UNKNOWN" } ], "tags": [ - "GatewayAccess" + "Configuration" ] } }, - "/gateways/{gateway_ids.gateway_id}/collaborator/organization/{collaborator.organization_ids.organization_id}": { + "/configuration/bands/{band_id}/{phy_version}": { "get": { - "operationId": "GatewayAccess_GetCollaborator2", + "operationId": "Configuration_ListBands3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetCollaboratorResponse" + "$ref": "#/definitions/v3ListBandsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", + "name": "band_id", + "description": "Optional Band ID to filter the results.\nIf unused, all supported Bands are returned.", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "phy_version", + "description": "Optional PHY version to filter the results.\nIf unused, all supported versions are returned.", "in": "path", "required": true, - "type": "string" - }, - { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, "type": "string", - "format": "byte" - }, - { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" + "enum": [ + "PHY_UNKNOWN", + "PHY_V1_0", + "TS001_V1_0", + "PHY_V1_0_1", + "TS001_V1_0_1", + "PHY_V1_0_2_REV_A", + "RP001_V1_0_2", + "PHY_V1_0_2_REV_B", + "RP001_V1_0_2_REV_B", + "PHY_V1_1_REV_A", + "RP001_V1_1_REV_A", + "PHY_V1_1_REV_B", + "RP001_V1_1_REV_B", + "PHY_V1_0_3_REV_A", + "RP001_V1_0_3_REV_A", + "RP002_V1_0_0", + "RP002_V1_0_1", + "RP002_V1_0_2", + "RP002_V1_0_3" + ] } ], "tags": [ - "GatewayAccess" + "Configuration" ] } }, - "/gateways/{gateway_ids.gateway_id}/collaborator/user/{collaborator.user_ids.user_id}": { + "/configuration/frequency-plans": { "get": { - "operationId": "GatewayAccess_GetCollaborator", + "operationId": "Configuration_ListFrequencyPlans", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetCollaboratorResponse" + "$ref": "#/definitions/v3ListFrequencyPlansResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "base_frequency", + "description": "Optional base frequency in MHz for hardware support (433, 470, 868 or 915)", "in": "query", "required": false, - "type": "string" + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "Configuration" + ] + } + }, + "/configuration/phy-versions": { + "get": { + "summary": "Returns a list of supported LoRaWAN PHY Versions for the given Band ID.", + "operationId": "Configuration_GetPhyVersions", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetPhyVersionsResponse" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "band_id", + "description": "Optional Band ID to filter the results.\nIf unused, all supported Bands and their versions are returned.", "in": "query", "required": false, "type": "string" } ], "tags": [ - "GatewayAccess" + "Configuration" ] } }, - "/gateways/{gateway_ids.gateway_id}/collaborators": { - "get": { - "operationId": "GatewayAccess_ListCollaborators", + "/contact_info/validation": { + "post": { + "summary": "Request validation for the non-validated contact info for the given entity.", + "operationId": "ContactInfoRegistry_RequestValidation", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Collaborators" + "$ref": "#/definitions/lorawanv3ContactInfoValidation" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_ids.gateway_id", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" - }, - { - "name": "gateway_ids.eui", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string", - "format": "byte" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "schema": { + "$ref": "#/definitions/v3EntityIdentifiers" + } } ], "tags": [ - "GatewayAccess" + "ContactInfoRegistry" ] }, - "put": { - "operationId": "GatewayAccess_SetCollaborator", + "patch": { + "summary": "Validate confirms a contact info validation.", + "operationId": "ContactInfoRegistry_Validate", "responses": { "200": { "description": "A successful response.", "schema": { + "type": "object", "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ - { - "name": "gateway_ids.gateway_id", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetGatewayCollaboratorRequest" + "$ref": "#/definitions/lorawanv3ContactInfoValidation" } } ], "tags": [ - "GatewayAccess" + "ContactInfoRegistry" ] } }, - "/gateways/{gateway_id}": { - "delete": { - "operationId": "GatewayRegistry_Delete", + "/dr/applications/{application_ids.application_id}/brands": { + "get": { + "operationId": "DeviceRepository_ListBrands2", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3ListEndDeviceBrandsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "limit", + "description": "Limit the number of results per page.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order_by", + "description": "Order (for pagination)", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "search", + "description": "Search for brands matching a query string.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "GatewayRegistry" + "DeviceRepository" ] } }, - "/gateways/{gateway_id}/rights": { + "/dr/applications/{application_ids.application_id}/brands/{brand_id}": { "get": { - "operationId": "GatewayAccess_ListRights", + "operationId": "DeviceRepository_GetBrand2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Rights" + "$ref": "#/definitions/v3EndDeviceBrand" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "brand_id", + "description": "Brand identifier, as defined in the Device Repository.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" } ], "tags": [ - "GatewayAccess" + "DeviceRepository" ] } }, - "/gs/gateways/{gateway_id}/connection/stats": { + "/dr/applications/{application_ids.application_id}/brands/{brand_id}/models": { "get": { - "operationId": "Gs_GetGatewayConnectionStats", + "operationId": "DeviceRepository_ListModels4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GatewayConnectionStats" + "$ref": "#/definitions/v3ListEndDeviceModelsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "brand_id", + "description": "List end devices from a specific brand.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order_by", + "description": "Order end devices", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "search", + "description": "List end devices matching a query string.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "Gs" + "DeviceRepository" ] } }, - "/gs/gateways/{gateway_id}/mqtt-connection-info": { + "/dr/applications/{application_ids.application_id}/brands/{brand_id}/models/{model_id}": { "get": { - "operationId": "GtwGs_GetMQTTConnectionInfo", + "operationId": "DeviceRepository_GetModel2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3MQTTConnectionInfo" + "$ref": "#/definitions/v3EndDeviceModel" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "brand_id", + "description": "Brand identifier, as defined in the Device Repository.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "model_id", + "description": "Model identifier, as defined in the Device Repository.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" } ], "tags": [ - "GtwGs" + "DeviceRepository" ] } }, - "/gs/gateways/{gateway_id}/mqttv2-connection-info": { + "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder": { "get": { - "operationId": "GtwGs_GetMQTTV2ConnectionInfo", + "operationId": "DeviceRepository_GetDownlinkDecoder2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3MQTTConnectionInfo" + "$ref": "#/definitions/v3MessagePayloadDecoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "gateway_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "eui", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "version_ids.brand_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.model_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.hardware_version", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "GtwGs" + "DeviceRepository" ] } }, - "/invitations": { + "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder": { "get": { - "operationId": "UserInvitationRegistry_List", + "operationId": "DeviceRepository_GetDownlinkEncoder2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Invitations" + "$ref": "#/definitions/v3MessagePayloadEncoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.brand_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.model_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.hardware_version", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, "type": "integer", "format": "int64" - } - ], - "tags": [ - "UserInvitationRegistry" - ] - }, - "delete": { - "operationId": "UserInvitationRegistry_Delete", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "properties": {} - } }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "email", + "name": "version_ids.serial_number", "in": "query", "required": false, "type": "string" - } - ], - "tags": [ - "UserInvitationRegistry" - ] - }, - "post": { - "operationId": "UserInvitationRegistry_Send", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/lorawanv3Invitation" - } }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3SendInvitationRequest" - } + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "UserInvitationRegistry" + "DeviceRepository" ] } }, - "/is/configuration": { + "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder": { "get": { - "operationId": "Is_GetConfiguration", + "operationId": "DeviceRepository_GetUplinkDecoder2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetIsConfigurationResponse" + "$ref": "#/definitions/v3MessagePayloadDecoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "tags": [ - "Is" - ] - } - }, - "/js/applications/{application_ids.application_id}/devices/{device_id}": { - "delete": { - "operationId": "JsEndDeviceRegistry_Delete", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "properties": {} - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -4095,277 +6801,256 @@ "type": "string" }, { - "name": "device_id", + "name": "version_ids.brand_id", "in": "path", "required": true, "type": "string" }, { - "name": "dev_eui", - "description": "The LoRaWAN DevEUI.", + "name": "version_ids.model_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.hardware_version", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" }, { - "name": "join_eui", - "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" }, { - "name": "dev_addr", - "description": "The LoRaWAN DevAddr.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, - "type": "string", - "format": "byte" - } - ], - "tags": [ - "JsEndDeviceRegistry" - ] - } - }, - "/js/applications/{application_ids.application_id}/provision-devices": { - "put": { - "operationId": "JsEndDeviceRegistry_Provision", - "responses": { - "200": { - "description": "A successful response.(streaming responses)", - "schema": { - "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/v3EndDevice" - }, - "error": { - "$ref": "#/definitions/runtimeStreamError" - } - }, - "title": "Stream result of v3EndDevice" - } + "type": "integer", + "format": "int64" }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "application_ids.application_id", - "in": "path", - "required": true, + "name": "version_ids.serial_number", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3ProvisionEndDevicesRequest" - } + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "JsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/js/applications/{end_device.ids.application_ids.application_id}/devices": { - "post": { - "operationId": "JsEndDeviceRegistry_Set2", + "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template": { + "get": { + "operationId": "DeviceRepository_GetTemplate3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3EndDeviceTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "end_device.ids.application_ids.application_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "version_ids.brand_id", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" - } - } - ], - "tags": [ - "JsEndDeviceRegistry" - ] - } - }, - "/js/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { - "put": { - "operationId": "JsEndDeviceRegistry_Set", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3EndDevice" - } + "type": "string" }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ { - "name": "end_device.ids.application_ids.application_id", + "name": "version_ids.model_id", "in": "path", "required": true, "type": "string" }, { - "name": "end_device.ids.device_id", + "name": "version_ids.firmware_version", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "version_ids.band_id", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" - } + "type": "string" + }, + { + "name": "version_ids.hardware_version", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "end_device_profile_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "end_device_profile_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" } ], "tags": [ - "JsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/js/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { + "/dr/applications/{application_ids.application_id}/models": { "get": { - "operationId": "JsEndDeviceRegistry_Get", + "operationId": "DeviceRepository_ListModels3", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3ListEndDeviceModelsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "end_device_ids.application_ids.application_id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "end_device_ids.device_id", - "in": "path", - "required": true, + "name": "brand_id", + "description": "List end devices from a specific brand.", + "in": "query", + "required": false, "type": "string" }, { - "name": "end_device_ids.dev_eui", - "description": "The LoRaWAN DevEUI.", + "name": "limit", + "description": "Limit the number of results per page.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" }, { - "name": "end_device_ids.join_eui", - "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" }, { - "name": "end_device_ids.dev_addr", - "description": "The LoRaWAN DevAddr.", + "name": "order_by", + "description": "Order end devices", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "search", + "description": "List end devices matching a query string.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "JsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/js/join_eui_prefixes": { + "/dr/applications/{application_ids.application_id}/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template": { "get": { - "operationId": "Js_GetJoinEUIPrefixes", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/v3JoinEUIPrefixes" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "tags": [ - "Js" - ] - } - }, - "/ns/applications/{application_ids.application_id}/devices/{device_id}": { - "delete": { - "operationId": "NsEndDeviceRegistry_Delete", + "operationId": "DeviceRepository_GetTemplate4", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EndDeviceTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -4377,481 +7062,669 @@ "type": "string" }, { - "name": "device_id", + "name": "end_device_profile_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "path", "required": true, - "type": "string" + "type": "integer", + "format": "int64" }, { - "name": "dev_eui", - "description": "The LoRaWAN DevEUI.", + "name": "end_device_profile_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.brand_id", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" }, { - "name": "join_eui", - "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "name": "version_ids.model_id", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" }, { - "name": "dev_addr", - "description": "The LoRaWAN DevAddr.", + "name": "version_ids.hardware_version", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "NsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/ns/applications/{end_device.ids.application_ids.application_id}/devices": { - "post": { - "operationId": "NsEndDeviceRegistry_Set2", + "/dr/brands": { + "get": { + "operationId": "DeviceRepository_ListBrands", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3ListEndDeviceBrandsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "end_device.ids.application_ids.application_id", - "in": "path", - "required": true, + "name": "application_ids.application_id", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" - } + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order_by", + "description": "Order (for pagination)", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "search", + "description": "Search for brands matching a query string.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "NsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/ns/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { - "put": { - "operationId": "NsEndDeviceRegistry_Set", + "/dr/brands/{brand_id}": { + "get": { + "operationId": "DeviceRepository_GetBrand", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3EndDeviceBrand" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "end_device.ids.application_ids.application_id", + "name": "brand_id", + "description": "Brand identifier, as defined in the Device Repository.", "in": "path", "required": true, "type": "string" }, { - "name": "end_device.ids.device_id", - "in": "path", - "required": true, + "name": "application_ids.application_id", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3SetEndDeviceRequest" - } + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "NsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { + "/dr/brands/{brand_id}/models": { "get": { - "operationId": "NsEndDeviceRegistry_Get", + "operationId": "DeviceRepository_ListModels2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3ListEndDeviceModelsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "end_device_ids.application_ids.application_id", + "name": "brand_id", + "description": "List end devices from a specific brand.", "in": "path", "required": true, "type": "string" }, { - "name": "end_device_ids.device_id", - "in": "path", - "required": true, + "name": "application_ids.application_id", + "in": "query", + "required": false, "type": "string" }, { - "name": "end_device_ids.dev_eui", - "description": "The LoRaWAN DevEUI.", + "name": "limit", + "description": "Limit the number of results per page.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" }, { - "name": "end_device_ids.join_eui", - "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "integer", + "format": "int64" }, { - "name": "end_device_ids.dev_addr", - "description": "The LoRaWAN DevAddr.", + "name": "order_by", + "description": "Order end devices", "in": "query", "required": false, - "type": "string", - "format": "byte" + "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "search", + "description": "List end devices matching a query string.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "NsEndDeviceRegistry" + "DeviceRepository" ] } }, - "/ns/dev_addr": { + "/dr/brands/{brand_id}/models/{model_id}": { "get": { - "operationId": "Ns_GenerateDevAddr", + "operationId": "DeviceRepository_GetModel", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GenerateDevAddrResponse" + "$ref": "#/definitions/v3EndDeviceModel" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, + "parameters": [ + { + "name": "brand_id", + "description": "Brand identifier, as defined in the Device Repository.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "model_id", + "description": "Model identifier, as defined in the Device Repository.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "application_ids.application_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" + } + ], "tags": [ - "Ns" + "DeviceRepository" ] } }, - "/organizations": { + "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder": { "get": { - "operationId": "OrganizationRegistry_List", + "operationId": "DeviceRepository_GetDownlinkDecoder", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organizations" + "$ref": "#/definitions/v3MessagePayloadDecoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, + "name": "version_ids.brand_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "query", - "required": false, + "name": "version_ids.model_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, + "name": "version_ids.firmware_version", + "in": "path", + "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "application_ids.application_id", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "version_ids.hardware_version", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, "type": "integer", "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "OrganizationRegistry" + "DeviceRepository" ] } }, - "/organizations/{collaborator.organization_ids.organization_id}/applications": { + "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder": { "get": { - "summary": "List applications where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the applications the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", - "operationId": "ApplicationRegistry_List3", + "operationId": "DeviceRepository_GetDownlinkEncoder", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Applications" + "$ref": "#/definitions/v3MessagePayloadEncoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "version_ids.brand_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "query", - "required": false, + "name": "version_ids.model_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "application_ids.application_id", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "version_ids.hardware_version", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, "type": "integer", "format": "int64" - } + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" + } ], "tags": [ - "ApplicationRegistry" + "DeviceRepository" ] - }, - "post": { - "summary": "Create a new application. This also sets the given organization or user as\nfirst collaborator with all possible rights.", - "operationId": "ApplicationRegistry_Create2", + } + }, + "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder": { + "get": { + "operationId": "DeviceRepository_GetUplinkDecoder", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Application" + "$ref": "#/definitions/v3MessagePayloadDecoder" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "version_ids.brand_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "version_ids.model_id", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3CreateApplicationRequest" - } + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "application_ids.application_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.hardware_version", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ApplicationRegistry" + "DeviceRepository" ] } }, - "/organizations/{collaborator.organization_ids.organization_id}/clients": { + "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template": { "get": { - "operationId": "ClientRegistry_List3", + "operationId": "DeviceRepository_GetTemplate", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Clients" + "$ref": "#/definitions/v3EndDeviceTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "version_ids.brand_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "version_ids.model_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.firmware_version", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "version_ids.band_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "application_ids.application_id", "in": "query", "required": false, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "version_ids.hardware_version", "in": "query", "required": false, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "integer", + "format": "int64" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "version_ids.serial_number", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "end_device_profile_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "end_device_profile_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, "type": "integer", @@ -4859,145 +7732,204 @@ } ], "tags": [ - "ClientRegistry" + "DeviceRepository" ] - }, - "post": { - "operationId": "ClientRegistry_Create2", + } + }, + "/dr/models": { + "get": { + "operationId": "DeviceRepository_ListModels", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Client" + "$ref": "#/definitions/v3ListEndDeviceModelsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "path", - "required": true, + "name": "application_ids.application_id", + "in": "query", + "required": false, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3CreateClientRequest" - } + "name": "brand_id", + "description": "List end devices from a specific brand.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order_by", + "description": "Order end devices", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "search", + "description": "List end devices matching a query string.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "Field mask paths.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "ClientRegistry" + "DeviceRepository" ] } }, - "/organizations/{collaborator.organization_ids.organization_id}/gateways": { + "/dr/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template": { "get": { - "operationId": "GatewayRegistry_List3", + "operationId": "DeviceRepository_GetTemplate2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateways" + "$ref": "#/definitions/v3EndDeviceTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "end_device_profile_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "end_device_profile_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "path", "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "application_ids.application_id", + "in": "query", + "required": false, "type": "string" }, { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "version_ids.brand_id", "in": "query", "required": false, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "version_ids.model_id", "in": "query", "required": false, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "version_ids.hardware_version", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "version_ids.firmware_version", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "version_ids.band_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "version_ids.vendor_id", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005.", "in": "query", "required": false, "type": "integer", "format": "int64" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "version_ids.vendor_profile_id", + "description": "ID of the LoRaWAN end device profile assigned by the vendor.", "in": "query", "required": false, "type": "integer", "format": "int64" + }, + { + "name": "version_ids.serial_number", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "GatewayRegistry" + "DeviceRepository" ] - }, + } + }, + "/edcs/applications/{application_ids.application_id}/authorize": { "post": { - "operationId": "GatewayRegistry_Create2", + "summary": "Authorize the End Device Claiming Server to claim devices registered in the given application. The application\nidentifiers are the source application, where the devices are registered before they are claimed.\nThe API key is used to access the application, find the device, verify the claim request and delete the end device\nfrom the source application.", + "operationId": "EndDeviceClaimingServer_AuthorizeApplication", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateway" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" @@ -5007,162 +7939,208 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateGatewayRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "api_key": { + "type": "string" + } + } } } ], "tags": [ - "GatewayRegistry" + "EndDeviceClaimingServer" ] } }, - "/organizations/{organization.ids.organization_id}": { - "put": { - "operationId": "OrganizationRegistry_Update", + "/edcs/applications/{application_id}/authorize": { + "delete": { + "summary": "Unauthorize the End Device Claiming Server to claim devices in the given application.\nThis reverts the authorization given with rpc AuthorizeApplication.", + "operationId": "EndDeviceClaimingServer_UnauthorizeApplication", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organization" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization.ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "application_id", "in": "path", "required": true, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3UpdateOrganizationRequest" - } } ], "tags": [ - "OrganizationRegistry" + "EndDeviceClaimingServer" ] } }, - "/organizations/{organization_ids.organization_id}": { - "get": { - "operationId": "OrganizationRegistry_Get", + "/edcs/claim": { + "post": { + "summary": "Claims the end device on a Join Server by claim authentication code or QR code.", + "operationId": "EndDeviceClaimingServer_Claim", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organization" + "$ref": "#/definitions/v3EndDeviceIdentifiers" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" + "schema": { + "$ref": "#/definitions/v3ClaimEndDeviceRequest" + } + } + ], + "tags": [ + "EndDeviceClaimingServer" + ] + } + }, + "/edcs/claim/info": { + "post": { + "summary": "Return whether claiming is available for a given JoinEUI.", + "operationId": "EndDeviceClaimingServer_GetInfoByJoinEUI", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetInfoByJoinEUIResponse" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3GetInfoByJoinEUIRequest" + } } ], "tags": [ - "OrganizationRegistry" + "EndDeviceClaimingServer" ] } }, - "/organizations/{organization_ids.organization_id}/api-keys": { + "/edcs/claim/{application_ids.application_id}/devices/{device_id}": { "get": { - "operationId": "OrganizationAccess_ListAPIKeys", + "summary": "Gets the claim status of an end device.", + "operationId": "EndDeviceClaimingServer_GetClaimStatus", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKeys" + "$ref": "#/definitions/v3GetClaimStatusResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "device_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" + }, + { + "name": "dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "OrganizationAccess" + "EndDeviceClaimingServer" ] }, - "post": { - "operationId": "OrganizationAccess_CreateAPIKey", + "delete": { + "summary": "Unclaims the end device on a Join Server.", + "operationId": "EndDeviceClaimingServer_Unclaim", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "device_id", "in": "path", "required": true, "type": "string" @@ -5172,167 +8150,227 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateOrganizationAPIKeyRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } } } ], "tags": [ - "OrganizationAccess" + "EndDeviceClaimingServer" ] } }, - "/organizations/{organization_ids.organization_id}/api-keys/{api_key.id}": { - "put": { - "operationId": "OrganizationAccess_UpdateAPIKey", + "/edtc/convert": { + "post": { + "summary": "Converts the binary data to a stream of end device templates.", + "operationId": "EndDeviceTemplateConverter_Convert", "responses": { "200": { - "description": "A successful response.", + "description": "A successful response.(streaming responses)", "schema": { - "$ref": "#/definitions/v3APIKey" + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v3EndDeviceTemplate" + }, + "error": { + "$ref": "#/definitions/googlerpcStatus" + } + }, + "title": "Stream result of v3EndDeviceTemplate" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ - { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "api_key.id", - "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateOrganizationAPIKeyRequest" + "$ref": "#/definitions/v3ConvertEndDeviceTemplateRequest" } } ], "tags": [ - "OrganizationAccess" + "EndDeviceTemplateConverter" ] } }, - "/organizations/{organization_ids.organization_id}/api-keys/{key_id}": { + "/edtc/formats": { "get": { - "operationId": "OrganizationAccess_GetAPIKey", + "summary": "Returns the configured formats to convert from.", + "operationId": "EndDeviceTemplateConverter_ListFormats", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "$ref": "#/definitions/v3EndDeviceTemplateFormats" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "path", - "required": true, - "type": "string" + "tags": [ + "EndDeviceTemplateConverter" + ] + } + }, + "/events": { + "post": { + "summary": "Stream live events, optionally with a tail of historical events (depending on server support and retention policy).\nEvents may arrive out-of-order.", + "operationId": "Events_Stream", + "responses": { + "200": { + "description": "A successful response.(streaming responses)", + "schema": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v3Event" + }, + "error": { + "$ref": "#/definitions/googlerpcStatus" + } + }, + "title": "Stream result of v3Event" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "key_id", - "description": "Unique public identifier for the API key.", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" + "schema": { + "$ref": "#/definitions/v3StreamEventsRequest" + } } ], "tags": [ - "OrganizationAccess" + "Events" ] } }, - "/organizations/{organization_ids.organization_id}/collaborator/user/{collaborator.user_ids.user_id}": { + "/events/related": { "get": { - "operationId": "OrganizationAccess_GetCollaborator", + "operationId": "Events_FindRelated", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GetCollaboratorResponse" + "$ref": "#/definitions/v3FindRelatedEventsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "correlation_id", "in": "query", "required": false, "type": "string" } ], "tags": [ - "OrganizationAccess" + "Events" ] } }, - "/organizations/{organization_ids.organization_id}/collaborators": { + "/gateways": { "get": { - "operationId": "OrganizationAccess_ListCollaborators", + "summary": "List gateways where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the gateways the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "GatewayRegistry_List", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Collaborators" + "$ref": "#/definitions/v3Gateways" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", + "name": "collaborator.organization_ids.organization_id", "description": "This ID shares namespace with user IDs.", - "in": "path", - "required": true, + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the gateway fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, "type": "string" }, { @@ -5350,32 +8388,41 @@ "required": false, "type": "integer", "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted gateways.", + "in": "query", + "required": false, + "type": "boolean" } ], "tags": [ - "OrganizationAccess" + "GatewayRegistry" ] - }, + } + }, + "/gateways/{gateway.ids.gateway_id}": { "put": { - "operationId": "OrganizationAccess_SetCollaborator", + "summary": "Update the gateway, changing the fields specified by the field mask to the provided values.", + "operationId": "GatewayRegistry_Update", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3Gateway" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "gateway.ids.gateway_id", "in": "path", "required": true, "type": "string" @@ -5385,395 +8432,647 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3SetOrganizationCollaboratorRequest" + "type": "object", + "properties": { + "gateway": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + }, + "description": "The identifiers of the gateway. These are public and can be seen by any authenticated user in the network.", + "title": "The identifiers of the gateway. These are public and can be seen by any authenticated user in the network." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the gateway was created. This information is public and can be seen by any authenticated user in the network." + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the gateway was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the gateway was deleted. This information is public and can be seen by any authenticated user in the network." + }, + "name": { + "type": "string", + "description": "The name of the gateway. This information is public and can be seen by any authenticated user in the network." + }, + "description": { + "type": "string", + "description": "A description for the gateway. This information is public and can be seen by any authenticated user in the network." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this gateway. Typically used for organizing gateways or for storing integration-specific data." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "version_ids": { + "$ref": "#/definitions/v3GatewayVersionIdentifiers" + }, + "gateway_server_address": { + "type": "string", + "description": "The address of the Gateway Server to connect to.\nThis information is public and can be seen by any authenticated user in the network if status_public is true.\nThe typical format of the address is \"scheme://host:port\". The scheme is optional. If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "auto_update": { + "type": "boolean" + }, + "update_channel": { + "type": "string" + }, + "frequency_plan_id": { + "type": "string", + "description": "Frequency plan ID of the gateway.\nThis information is public and can be seen by any authenticated user in the network.\nDEPRECATED: use frequency_plan_ids.\nThis equals the first element of the frequency_plan_ids field." + }, + "frequency_plan_ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Frequency plan IDs of the gateway.\nThis information is public and can be seen by any authenticated user in the network.\nThe first element equals the frequency_plan_id field." + }, + "antennas": { + "type": "array", + "items": { + "$ref": "#/definitions/v3GatewayAntenna" + }, + "description": "Antennas of the gateway. Location information of the antennas is public and can be seen by any authenticated user in the network if location_public=true." + }, + "status_public": { + "type": "boolean", + "description": "The status of this gateway may be publicly displayed." + }, + "location_public": { + "type": "boolean", + "description": "The location of this gateway may be publicly displayed." + }, + "schedule_downlink_late": { + "type": "boolean", + "description": "Enable server-side buffering of downlink messages. This is recommended for gateways using the Semtech UDP Packet\nForwarder v2.x or older, as it does not feature a just-in-time queue. If enabled, the Gateway Server schedules the\ndownlink message late to the gateway so that it does not overwrite previously scheduled downlink messages that have\nnot been transmitted yet." + }, + "enforce_duty_cycle": { + "type": "boolean", + "description": "Enforcing gateway duty cycle is recommended for all gateways to respect spectrum regulations. Disable enforcing the\nduty cycle only in controlled research and development environments." + }, + "downlink_path_constraint": { + "$ref": "#/definitions/v3DownlinkPathConstraint" + }, + "schedule_anytime_delay": { + "type": "string", + "description": "Adjust the time that GS schedules class C messages in advance. This is useful for gateways that have a known high latency backhaul, like 3G and satellite." + }, + "update_location_from_status": { + "type": "boolean", + "description": "Update the location of this gateway from status messages. This only works for gateways connecting with authentication; gateways connected over UDP are not supported." + }, + "lbs_lns_secret": { + "$ref": "#/definitions/v3Secret", + "description": "The LoRa Basics Station LNS secret.\nThis is either an auth token (such as an API Key) or a TLS private certificate.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3GatewayClaimAuthenticationCode", + "description": "The authentication code for gateway claiming.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value.\nThe entire field must be used in RPCs since sub-fields are validated wrt to each other. Direct selection/update of sub-fields only are not allowed.\nUse the top level field mask `claim_authentication_code` even when updating single fields." + }, + "target_cups_uri": { + "type": "string", + "description": "CUPS URI for LoRa Basics Station CUPS redirection.\nThe CUPS Trust field will be automatically fetched from the cert chain presented by the target server." + }, + "target_cups_key": { + "$ref": "#/definitions/v3Secret", + "description": "CUPS Key for LoRa Basics Station CUPS redirection.\nIf redirecting to another instance of TTS, use the CUPS API Key for the gateway on the target instance.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value." + }, + "require_authenticated_connection": { + "type": "boolean", + "description": "Require an authenticated gateway connection. This prevents the gateway from using the UDP protocol and requires authentication when using other protocols." + }, + "lrfhss": { + "$ref": "#/definitions/GatewayLRFHSS" + }, + "disable_packet_broker_forwarding": { + "type": "boolean" + } + }, + "description": "Gateway is the message that defines a gateway on the network." + }, + "field_mask": { + "type": "string", + "description": "The names of the gateway fields that should be updated." + } + } } } ], "tags": [ - "OrganizationAccess" + "GatewayRegistry" ] } }, - "/organizations/{organization_id}": { - "delete": { - "operationId": "OrganizationRegistry_Delete", + "/gateways/{gateway_ids.gateway_id}": { + "get": { + "summary": "Get the gateway with the given identifiers, selecting the fields specified\nin the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "GatewayRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3Gateway" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "field_mask", + "description": "The names of the gateway fields that should be returned.", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ - "OrganizationRegistry" + "GatewayRegistry" ] } }, - "/organizations/{organization_id}/rights": { + "/gateways/{gateway_ids.gateway_id}/api-keys": { "get": { - "operationId": "OrganizationAccess_ListRights", + "summary": "List the API keys for this gateway.", + "operationId": "GatewayAccess_ListAPIKeys", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Rights" + "$ref": "#/definitions/v3APIKeys" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "organization_id", - "description": "This ID shares namespace with user IDs.", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "order", + "description": "Order the results by this field path.\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" } ], "tags": [ - "OrganizationAccess" + "GatewayAccess" ] - } - }, - "/qr-codes/end-devices": { + }, "post": { - "operationId": "EndDeviceQRCodeGenerator_Generate", + "summary": "Create an API key scoped to this gateway.", + "operationId": "GatewayAccess_CreateAPIKey", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3GenerateQRCodeResponse" + "$ref": "#/definitions/v3APIKey" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ + { + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, + "type": "string" + }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3GenerateEndDeviceQRCodeRequest" + "type": "object", + "properties": { + "gateway_ids": { + "type": "object", + "properties": { + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "name": { + "type": "string" + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + } + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } } } ], "tags": [ - "EndDeviceQRCodeGenerator" + "GatewayAccess" ] } }, - "/qr-codes/end-devices/formats": { - "get": { - "operationId": "EndDeviceQRCodeGenerator_ListFormats", + "/gateways/{gateway_ids.gateway_id}/api-keys/{api_key.id}": { + "put": { + "summary": "Update the rights of an API key of the gateway.\nThis method can also be used to delete the API key, by giving it no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "GatewayAccess_UpdateAPIKey", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3QRCodeFormats" + "$ref": "#/definitions/v3APIKey" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, + "parameters": [ + { + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "api_key.id", + "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "gateway_ids": { + "type": "object", + "properties": { + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "api_key": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Immutable and unique secret value of the API key.\nGenerated by the Access Server." + }, + "name": { + "type": "string", + "description": "User-defined (friendly) name for the API key." + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Rights that are granted to this API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the api key fields that should be updated." + } + } + } + } + ], "tags": [ - "EndDeviceQRCodeGenerator" + "GatewayAccess" ] } }, - "/qr-codes/end-devices/formats/{format_id}": { + "/gateways/{gateway_ids.gateway_id}/api-keys/{key_id}": { "get": { - "operationId": "EndDeviceQRCodeGenerator_GetFormat", + "summary": "Get a single API key of this gateway.", + "operationId": "GatewayAccess_GetAPIKey", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3QRCodeFormat" + "$ref": "#/definitions/v3APIKey" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "format_id", + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "key_id", + "description": "Unique public identifier for the API key.", "in": "path", "required": true, "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "EndDeviceQRCodeGenerator" + "GatewayAccess" ] } }, - "/search/applications": { + "/gateways/{gateway_ids.gateway_id}/collaborator/organization/{collaborator.organization_ids.organization_id}": { "get": { - "operationId": "EntityRegistrySearch_SearchApplications", + "summary": "Get the rights of a collaborator (member) of the gateway.\nPseudo-rights in the response (such as the \"_ALL\" right) are not expanded.", + "operationId": "GatewayAccess_GetCollaborator2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Applications" + "$ref": "#/definitions/v3GetCollaboratorResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "id_contains", - "description": "Find entities where the ID contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "name_contains", - "description": "Find entities where the name contains this substring.", - "in": "query", - "required": false, + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "description_contains", - "description": "Find entities where the description contains this substring.", - "in": "query", - "required": false, + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string", + "format": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string" } ], "tags": [ - "EntityRegistrySearch" + "GatewayAccess" ] } }, - "/search/applications/{application_ids.application_id}/devices": { + "/gateways/{gateway_ids.gateway_id}/collaborator/user/{collaborator.user_ids.user_id}": { "get": { - "operationId": "EndDeviceRegistrySearch_SearchEndDevices", + "summary": "Get the rights of a collaborator (member) of the gateway.\nPseudo-rights in the response (such as the \"_ALL\" right) are not expanded.", + "operationId": "GatewayAccess_GetCollaborator", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3EndDevices" + "$ref": "#/definitions/v3GetCollaboratorResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "application_ids.application_id", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "id_contains", - "description": "Find end devices where the ID contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "name_contains", - "description": "Find end devices where the name contains this substring.", - "in": "query", - "required": false, + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, "type": "string" }, { - "name": "description_contains", - "description": "Find end devices where the description contains this substring.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "string" + "type": "string", + "format": "string" }, { - "name": "dev_eui_contains", - "description": "Find end devices where the (hexadecimal) DevEUI contains this substring.", + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", "in": "query", "required": false, "type": "string" }, { - "name": "join_eui_contains", - "description": "Find end devices where the (hexadecimal) JoinEUI contains this substring.", + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, "type": "string" - }, - { - "name": "dev_addr_contains", - "description": "Find end devices where the (hexadecimal) DevAddr contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" } ], "tags": [ - "EndDeviceRegistrySearch" + "GatewayAccess" ] } }, - "/search/clients": { + "/gateways/{gateway_ids.gateway_id}/collaborators": { "get": { - "operationId": "EntityRegistrySearch_SearchClients", + "summary": "List the collaborators on this gateway.", + "operationId": "GatewayAccess_ListCollaborators", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Clients" + "$ref": "#/definitions/v3Collaborators" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "id_contains", - "description": "Find entities where the ID contains this substring.", - "in": "query", - "required": false, + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "name_contains", - "description": "Find entities where the name contains this substring.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "string" + "type": "string", + "format": "string" }, { - "name": "description_contains", - "description": "Find entities where the description contains this substring.", + "name": "limit", + "description": "Limit the number of results per page.", "in": "query", "required": false, - "type": "string" + "type": "integer", + "format": "int64" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "integer", + "format": "int64" }, { "name": "order", @@ -5781,101 +9080,130 @@ "in": "query", "required": false, "type": "string" + } + ], + "tags": [ + "GatewayAccess" + ] + }, + "put": { + "summary": "Set the rights of a collaborator (member) on the gateway.\nThis method can also be used to delete the collaborator, by giving them no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "GatewayAccess_SetCollaborator", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, + "type": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "gateway_ids": { + "type": "object", + "properties": { + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "collaborator": { + "$ref": "#/definitions/v3Collaborator" + } + } + } } ], "tags": [ - "EntityRegistrySearch" + "GatewayAccess" ] } }, - "/search/gateways": { + "/gateways/{gateway_ids.gateway_id}/collaborators/search": { "get": { - "operationId": "EntityRegistrySearch_SearchGateways", + "summary": "Search for accounts that match the conditions specified in the request.", + "operationId": "EntityRegistrySearch_SearchAccounts4", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateways" + "$ref": "#/definitions/v3SearchAccountsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "id_contains", - "description": "Find entities where the ID contains this substring.", - "in": "query", - "required": false, + "name": "gateway_ids.gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "name_contains", - "description": "Find entities where the name contains this substring.", + "name": "query", "in": "query", "required": false, "type": "string" }, { - "name": "description_contains", - "description": "Find entities where the description contains this substring.", + "name": "only_users", "in": "query", "required": false, - "type": "string" + "type": "boolean" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "application_ids.application_id", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "client_ids.client_id", "in": "query", "required": false, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string" } ], "tags": [ @@ -5883,234 +9211,181 @@ ] } }, - "/search/organizations": { - "get": { - "operationId": "EntityRegistrySearch_SearchOrganizations", + "/gateways/{gateway_id}": { + "delete": { + "summary": "Delete the gateway. This may not release the gateway ID for reuse, but it does release the EUI.", + "operationId": "GatewayRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organizations" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "id_contains", - "description": "Find entities where the ID contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "name_contains", - "description": "Find entities where the name contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "description_contains", - "description": "Find entities where the description contains this substring.", - "in": "query", - "required": false, + "name": "gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string", + "format": "string" + } + ], + "tags": [ + "GatewayRegistry" + ] + } + }, + "/gateways/{gateway_id}/purge": { + "delete": { + "summary": "Purge the gateway. This will release both gateway ID and EUI for reuse.\nThe gateway owner is responsible for clearing data from any (external) integrations\nthat may store and expose data by gateway ID.", + "operationId": "GatewayRegistry_Purge", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, + "name": "gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" } ], "tags": [ - "EntityRegistrySearch" + "GatewayRegistry" ] } }, - "/search/users": { - "get": { - "operationId": "EntityRegistrySearch_SearchUsers", + "/gateways/{gateway_id}/restore": { + "post": { + "summary": "Restore a recently deleted gateway. This does not restore the EUI,\nas that was released when deleting the gateway.", + "description": "Deployment configuration may specify if, and for how long after deletion,\nentities can be restored.", + "operationId": "GatewayRegistry_Restore", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Users" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "id_contains", - "description": "Find entities where the ID contains this substring.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "name_contains", - "description": "Find entities where the name contains this substring.", - "in": "query", - "required": false, + "name": "gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "description_contains", - "description": "Find entities where the description contains this substring.", + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" } ], "tags": [ - "EntityRegistrySearch" + "GatewayRegistry" ] } }, - "/users": { + "/gateways/{gateway_id}/rights": { "get": { - "operationId": "UserRegistry_List", + "summary": "List the rights the caller has on this gateway.", + "operationId": "GatewayAccess_ListRights", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Users" + "$ref": "#/definitions/v3Rights" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, + "name": "gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" } ], "tags": [ - "UserRegistry" + "GatewayAccess" ] - }, + } + }, + "/gcls/claim": { "post": { - "operationId": "UserRegistry_Create", + "summary": "Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user.", + "operationId": "GatewayClaimingServer_Claim", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3User" + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, @@ -6120,422 +9395,392 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateUserRequest" + "$ref": "#/definitions/v3ClaimGatewayRequest" } } ], "tags": [ - "UserRegistry" + "GatewayClaimingServer" ] } }, - "/users/{collaborator.user_ids.user_id}/applications": { - "get": { - "summary": "List applications where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the applications the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", - "operationId": "ApplicationRegistry_List2", + "/gcls/gateways/{gateway_ids.gateway_id}/authorize": { + "post": { + "summary": "AuthorizeGateway allows a gateway to be claimed.", + "operationId": "GatewayClaimingServer_AuthorizeGateway", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Applications" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "gateway_ids": { + "type": "object", + "properties": { + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "api_key": { + "type": "string" + } + } + } } ], "tags": [ - "ApplicationRegistry" + "GatewayClaimingServer" ] - }, - "post": { - "summary": "Create a new application. This also sets the given organization or user as\nfirst collaborator with all possible rights.", - "operationId": "ApplicationRegistry_Create", + } + }, + "/gcls/gateways/{gateway_id}/authorize": { + "delete": { + "summary": "UnauthorizeGateway prevents a gateway from being claimed.", + "operationId": "GatewayClaimingServer_UnauthorizeGateway", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Application" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3CreateApplicationRequest" - } + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "ApplicationRegistry" + "GatewayClaimingServer" ] } }, - "/users/{collaborator.user_ids.user_id}/clients": { + "/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{filename}": { "get": { - "operationId": "ClientRegistry_List2", + "operationId": "GatewayConfigurationService_GetGatewayConfiguration", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Clients" + "$ref": "#/definitions/v3GetGatewayConfigurationResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, + "name": "format", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, + "name": "filename", + "in": "path", + "required": true, "type": "string" }, { - "name": "field_mask.paths", - "description": "The set of field mask paths.", + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" + "type": "string", + "format": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "type", "in": "query", "required": false, "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" } ], "tags": [ - "ClientRegistry" + "GatewayConfigurationService" ] - }, - "post": { - "operationId": "ClientRegistry_Create", + } + }, + "/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{type}/{filename}": { + "get": { + "operationId": "GatewayConfigurationService_GetGatewayConfiguration2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Client" + "$ref": "#/definitions/v3GetGatewayConfigurationResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_ids.gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", + "name": "format", + "in": "path", "required": true, - "schema": { - "$ref": "#/definitions/v3CreateClientRequest" - } + "type": "string" + }, + { + "name": "type", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "filename", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "ClientRegistry" + "GatewayConfigurationService" ] } }, - "/users/{collaborator.user_ids.user_id}/gateways": { - "get": { - "operationId": "GatewayRegistry_List2", + "/gs/gateways/connection/stats": { + "post": { + "summary": "Get statistics about gateway connections to the Gateway Server of a batch of gateways.\nThis is not persisted between reconnects.\nGateways that are not connected or are part of a different cluster are ignored.\nIt is up to the client to make sure that the gateways are in the requested cluster.", + "operationId": "Gs_BatchGetGatewayConnectionStats", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateways" + "$ref": "#/definitions/v3BatchGetGatewayConnectionStatsResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", + "name": "body", + "in": "body", "required": true, - "type": "string" + "schema": { + "$ref": "#/definitions/v3BatchGetGatewayConnectionStatsRequest" + } + } + ], + "tags": [ + "Gs" + ] + } + }, + "/gs/gateways/{gateway_id}/connection/stats": { + "get": { + "summary": "Get statistics about the current gateway connection to the Gateway Server.\nThis is not persisted between reconnects.", + "operationId": "Gs_GetGatewayConnectionStats", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GatewayConnectionStats" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, + "name": "gateway_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "collaborator.user_ids.email", + "name": "eui", "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" } ], "tags": [ - "GatewayRegistry" + "Gs" ] - }, - "post": { - "operationId": "GatewayRegistry_Create", + } + }, + "/gs/gateways/{gateway_id}/mqtt-connection-info": { + "get": { + "summary": "Get connection information to connect an MQTT gateway.", + "operationId": "GtwGs_GetMQTTConnectionInfo", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Gateway" + "$ref": "#/definitions/v3MQTTConnectionInfo" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3CreateGatewayRequest" - } + "name": "eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "GatewayRegistry" + "GtwGs" ] } }, - "/users/{collaborator.user_ids.user_id}/organizations": { + "/gs/gateways/{gateway_id}/mqttv2-connection-info": { "get": { - "operationId": "OrganizationRegistry_List2", + "summary": "Get legacy connection information to connect a The Things Network Stack V2 MQTT gateway.", + "operationId": "GtwGs_GetMQTTV2ConnectionInfo", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organizations" + "$ref": "#/definitions/v3MQTTConnectionInfo" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "gateway_id", "in": "path", "required": true, "type": "string" }, { - "name": "collaborator.organization_ids.organization_id", - "description": "This ID shares namespace with user IDs.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "collaborator.user_ids.email", + "name": "eui", "description": "Secondary identifier, which can only be used in specific requests.", "in": "query", "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" + "type": "string", + "format": "string" + } + ], + "tags": [ + "GtwGs" + ] + } + }, + "/invitations": { + "get": { + "summary": "List the invitations the caller has sent.", + "operationId": "UserInvitationRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Invitations" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { "name": "limit", "description": "Limit the number of results per page.", @@ -6554,208 +9799,186 @@ } ], "tags": [ - "OrganizationRegistry" + "UserInvitationRegistry" ] }, - "post": { - "operationId": "OrganizationRegistry_Create", + "delete": { + "summary": "Delete (revoke) a user invitation.", + "operationId": "UserInvitationRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Organization" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "collaborator.user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, + "name": "email", + "in": "query", + "required": false, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3CreateOrganizationRequest" - } } ], "tags": [ - "OrganizationRegistry" + "UserInvitationRegistry" ] - } - }, - "/users/{user.ids.user_id}": { - "put": { - "operationId": "UserRegistry_Update", + }, + "post": { + "summary": "Invite a user to join the network.", + "operationId": "UserInvitationRegistry_Send", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3User" + "$ref": "#/definitions/lorawanv3Invitation" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ - { - "name": "user.ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateUserRequest" + "$ref": "#/definitions/v3SendInvitationRequest" } } ], "tags": [ - "UserRegistry" + "UserInvitationRegistry" ] } }, - "/users/{user_ids.user_id}": { + "/is/configuration": { "get": { - "operationId": "UserRegistry_Get", + "summary": "Get the configuration of the Identity Server. The response is typically used\nto enable or disable features in a user interface.", + "operationId": "Is_GetConfiguration", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3User" + "$ref": "#/definitions/v3GetIsConfigurationResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "field_mask.paths", - "description": "The set of field mask paths.", - "in": "query", - "required": false, - "type": "array", - "items": { - "type": "string" - }, - "collectionFormat": "multi" - } - ], "tags": [ - "UserRegistry" + "Is" ] } }, - "/users/{user_ids.user_id}/api-keys": { - "get": { - "operationId": "UserAccess_ListAPIKeys", + "/js/applications/{application_ids.application_id}/devices/{device_id}": { + "delete": { + "summary": "Delete deletes the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "operationId": "JsEndDeviceRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKeys" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, + "name": "device_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" + }, + { + "name": "dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "UserAccess" + "JsEndDeviceRegistry" ] - }, - "post": { - "operationId": "UserAccess_CreateAPIKey", + } + }, + "/js/applications/{application_ids.application_id}/provision-devices": { + "put": { + "summary": "This rpc is deprecated; use EndDeviceTemplateConverter service instead.\nTODO: Remove (https://github.com/TheThingsNetwork/lorawan-stack/issues/999)", + "operationId": "JsEndDeviceRegistry_Provision", "responses": { "200": { - "description": "A successful response.", + "description": "A successful response.(streaming responses)", "schema": { - "$ref": "#/definitions/v3APIKey" + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/v3EndDevice" + }, + "error": { + "$ref": "#/definitions/googlerpcStatus" + } + }, + "title": "Stream result of v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" @@ -6765,349 +9988,968 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3CreateUserAPIKeyRequest" + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner service as configured in the Join Server." + }, + "provisioning_data": { + "type": "string", + "format": "byte", + "description": "Vendor-specific provisioning data." + }, + "list": { + "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersList", + "description": "List of device identifiers that will be provisioned.\nThe device identifiers must contain device_id and dev_eui.\nIf set, the application_ids must equal the provision request's application_ids.\nThe number of entries in data must match the number of given identifiers." + }, + "range": { + "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersRange", + "description": "Provision devices in a range.\nThe device_id will be generated by the provisioner from the vendor-specific data.\nThe dev_eui will be issued from the given start_dev_eui." + }, + "from_data": { + "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersFromData", + "description": "Provision devices with identifiers from the given data.\nThe device_id and dev_eui will be generated by the provisioner from the vendor-specific data." + } + } } } ], "tags": [ - "UserAccess" + "JsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/api-keys/{api_key.id}": { - "put": { - "operationId": "UserAccess_UpdateAPIKey", + "/js/applications/{application_ids.application_id}/settings": { + "get": { + "summary": "Get returns application activation settings.", + "operationId": "ApplicationActivationSettingRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "$ref": "#/definitions/v3ApplicationActivationSettings" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "api_key.id", - "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", - "in": "path", - "required": true, + "name": "field_mask", + "in": "query", + "required": false, "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v3UpdateUserAPIKeyRequest" - } } ], "tags": [ - "UserAccess" + "ApplicationActivationSettingRegistry" ] - } - }, - "/users/{user_ids.user_id}/api-keys/{key_id}": { - "get": { - "operationId": "UserAccess_GetAPIKey", + }, + "delete": { + "summary": "Delete deletes application activation settings.", + "operationId": "ApplicationActivationSettingRegistry_Delete", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3APIKey" + "type": "object", + "properties": {} } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" + } + ], + "tags": [ + "ApplicationActivationSettingRegistry" + ] + }, + "post": { + "summary": "Set creates or updates application activation settings.", + "operationId": "ApplicationActivationSettingRegistry_Set", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ApplicationActivationSettings" + } }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "key_id", - "description": "Unique public identifier for the API key.", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "settings": { + "$ref": "#/definitions/v3ApplicationActivationSettings" + }, + "field_mask": { + "type": "string" + } + } + } } ], "tags": [ - "UserAccess" + "ApplicationActivationSettingRegistry" ] } }, - "/users/{user_ids.user_id}/authorizations": { - "get": { - "operationId": "OAuthAuthorizationRegistry_List", + "/js/applications/{end_device.ids.application_ids.application_id}/devices": { + "post": { + "summary": "Set creates or updates the device.", + "operationId": "JsEndDeviceRegistry_Set2", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3OAuthClientAuthorizations" + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "device_id": { + "type": "string" + }, + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } + } } ], "tags": [ - "OAuthAuthorizationRegistry" + "JsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}": { - "delete": { - "operationId": "OAuthAuthorizationRegistry_Delete", + "/js/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { + "put": { + "summary": "Set creates or updates the device.", + "operationId": "JsEndDeviceRegistry_Set", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "client_ids.client_id", + "name": "end_device.ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } + } } ], "tags": [ - "OAuthAuthorizationRegistry" + "JsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens": { + "/js/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { "get": { - "operationId": "OAuthAuthorizationRegistry_ListTokens", + "summary": "Get returns the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "operationId": "JsEndDeviceRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3OAuthAccessTokens" + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "client_ids.client_id", + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "string" + "type": "string", + "format": "string" }, { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "name": "end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", "in": "query", "required": false, - "type": "string" + "type": "string", + "format": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", + "name": "end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string", + "format": "string" }, { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", + "name": "field_mask", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services.", "in": "query", "required": false, - "type": "integer", - "format": "int64" + "type": "string" } ], "tags": [ - "OAuthAuthorizationRegistry" + "JsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens/{id}": { - "delete": { - "operationId": "OAuthAuthorizationRegistry_DeleteToken", + "/js/default_join_eui": { + "get": { + "summary": "Request the default JoinEUI that is configured for this Join Server.", + "operationId": "Js_GetDefaultJoinEUI", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3GetDefaultJoinEUIResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "client_ids.client_id", - "in": "path", - "required": true, - "type": "string" - }, + "tags": [ + "Js" + ] + } + }, + "/js/join_eui_prefixes": { + "get": { + "summary": "Request the JoinEUI prefixes that are configured for this Join Server.", + "operationId": "Js_GetJoinEUIPrefixes", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3JoinEUIPrefixes" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Js" + ] + } + }, + "/ns/applications/{application_ids.application_id}/devices/{device_id}": { + "delete": { + "summary": "Delete deletes the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "operationId": "NsEndDeviceRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ { - "name": "id", + "name": "application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "device_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "dev_eui", + "description": "The LoRaWAN DevEUI.", "in": "query", "required": false, - "type": "string" + "type": "string", + "format": "string" + }, + { + "name": "join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" } ], "tags": [ - "OAuthAuthorizationRegistry" + "NsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/password": { - "put": { - "operationId": "UserRegistry_UpdatePassword", + "/ns/applications/{end_device.ids.application_ids.application_id}/devices": { + "post": { + "summary": "Set creates or updates the device.", + "operationId": "NsEndDeviceRegistry_Set2", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" @@ -7117,238 +10959,5889 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v3UpdateUserPasswordRequest" + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "device_id": { + "type": "string" + }, + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } } } ], "tags": [ - "UserRegistry" + "NsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/sessions": { - "get": { - "operationId": "UserSessionRegistry_List", + "/ns/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}": { + "put": { + "summary": "Set creates or updates the device.", + "operationId": "NsEndDeviceRegistry_Set", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3UserSessions" + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device.ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - }, - { - "name": "order", - "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", - "in": "query", - "required": false, + "name": "end_device.ids.device_id", + "in": "path", + "required": true, "type": "string" }, { - "name": "limit", - "description": "Limit the number of results per page.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" - }, - { - "name": "page", - "description": "Page number for pagination. 0 is interpreted as 1.", - "in": "query", - "required": false, - "type": "integer", - "format": "int64" + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "name": { + "type": "string", + "description": "Friendly name of the device. Stored in Entity Registry." + }, + "description": { + "type": "string", + "description": "Description of the device. Stored in Entity Registry." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "Version Identifiers. Stored in Entity Registry, Network Server and Application Server." + }, + "service_profile_id": { + "type": "string", + "description": "Default service profile. Stored in Entity Registry." + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "network_server_kek_label": { + "type": "string", + "description": "The KEK label of the Network Server to use for wrapping network session keys.\nStored in Join Server." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this device is supposed to be registered.\nStored in Entity Registry and Join Server.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_kek_label": { + "type": "string", + "description": "The KEK label of the Application Server to use for wrapping the application session key.\nStored in Join Server." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use.\nStored in Join Server." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this device is supposed to be registered.\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "Location of the device. Stored in Entity Registry." + }, + "picture": { + "$ref": "#/definitions/v3Picture", + "description": "Stored in Entity Registry." + }, + "supports_class_b": { + "type": "boolean", + "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_class_c": { + "type": "boolean", + "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_version": { + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN MAC version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "lorawan_phy_version": { + "$ref": "#/definitions/v3PHYVersion", + "description": "LoRaWAN PHY version. Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "frequency_plan_id": { + "type": "string", + "description": "ID of the frequency plan used by this device.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "min_frequency": { + "type": "string", + "format": "uint64", + "description": "Minimum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "max_frequency": { + "type": "string", + "format": "uint64", + "description": "Maximum frequency the device is capable of using (Hz). Stored in Network Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "supports_join": { + "type": "boolean", + "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "resets_join_nonces": { + "type": "boolean", + "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." + }, + "root_keys": { + "$ref": "#/definitions/v3RootKeys", + "description": "Device root keys. Stored in Join Server." + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID. Stored in Join Server." + }, + "mac_settings": { + "$ref": "#/definitions/v3MACSettings", + "description": "Settings for how the Network Server handles MAC layer for this device. Stored in Network Server." + }, + "mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "MAC state of the device. Stored in Network Server." + }, + "pending_mac_state": { + "$ref": "#/definitions/v3MACState", + "description": "Pending MAC state of the device. Stored in Network Server." + }, + "session": { + "$ref": "#/definitions/v3Session", + "description": "Current session of the device. Stored in Network Server and Application Server." + }, + "pending_session": { + "$ref": "#/definitions/v3Session", + "description": "Pending session. Stored in Network Server and Application Server until RekeyInd is received." + }, + "last_dev_nonce": { + "type": "integer", + "format": "int64", + "description": "Last DevNonce used.\nThis field is only used for devices using LoRaWAN version 1.1 and later.\nStored in Join Server." + }, + "used_dev_nonces": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Used DevNonces sorted in ascending order.\nThis field is only used for devices using LoRaWAN versions preceding 1.1.\nStored in Join Server." + }, + "last_join_nonce": { + "type": "integer", + "format": "int64", + "description": "Last JoinNonce/AppNonce(for devices using LoRaWAN versions preceding 1.1) used.\nStored in Join Server." + }, + "last_rj_count_0": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 0/2).\nStored in Join Server." + }, + "last_rj_count_1": { + "type": "integer", + "format": "int64", + "description": "Last Rejoin counter value used (type 1).\nStored in Join Server." + }, + "last_dev_status_received_at": { + "type": "string", + "format": "date-time", + "description": "Time when last DevStatus MAC command was received.\nStored in Network Server." + }, + "power_state": { + "$ref": "#/definitions/v3PowerState", + "description": "The power state of the device; whether it is battery-powered or connected to an external power source.\nReceived via the DevStatus MAC command at status_received_at.\nStored in Network Server." + }, + "battery_percentage": { + "type": "number", + "format": "float", + "description": "Latest-known battery percentage of the device.\nReceived via the DevStatus MAC command at last_dev_status_received_at or earlier.\nStored in Network Server." + }, + "downlink_margin": { + "type": "integer", + "format": "int32", + "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." + }, + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server,\nwhich sets them on the Network Server.\nThis field is deprecated and is always set equal to session.queued_application_downlinks." + }, + "formatters": { + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "The payload formatters for this end device. Stored in Application Server.\nCopied on creation from template identified by version_ids." + }, + "provisioner_id": { + "type": "string", + "description": "ID of the provisioner. Stored in Join Server." + }, + "provisioning_data": { + "type": "object", + "description": "Vendor-specific provisioning data. Stored in Join Server." + }, + "multicast": { + "type": "boolean", + "description": "Indicates whether this device represents a multicast group." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3EndDeviceAuthenticationCode", + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." + }, + "skip_payload_crypto": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." + }, + "skip_payload_crypto_override": { + "type": "boolean", + "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." + } + }, + "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + } + } + } } ], "tags": [ - "UserSessionRegistry" + "NsEndDeviceRegistry" ] } }, - "/users/{user_ids.user_id}/sessions/{session_id}": { - "delete": { - "operationId": "UserSessionRegistry_Delete", + "/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}": { + "get": { + "summary": "Get returns the device that matches the given identifiers.\nIf there are multiple matches, an error will be returned.", + "operationId": "NsEndDeviceRegistry_Get", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device_ids.application_ids.application_id", "in": "path", "required": true, "type": "string" }, { - "name": "session_id", + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" }, { - "name": "user_ids.email", - "description": "Secondary identifier, which can only be used in specific requests.", + "name": "end_device_ids.dev_eui", + "description": "The LoRaWAN DevEUI.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.join_eui", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices).", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "end_device_ids.dev_addr", + "description": "The LoRaWAN DevAddr.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "field_mask", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services.", "in": "query", "required": false, "type": "string" } ], "tags": [ - "UserSessionRegistry" + "NsEndDeviceRegistry" ] - } - }, - "/users/{user_ids.user_id}/temporary_password": { - "post": { - "operationId": "UserRegistry_CreateTemporaryPassword", + }, + "patch": { + "summary": "ResetFactoryDefaults resets device state to factory defaults.", + "operationId": "NsEndDeviceRegistry_ResetFactoryDefaults", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3EndDevice" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_ids.user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "end_device_ids.application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "end_device_ids.device_id", "in": "path", "required": true, "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "end_device_ids": { + "type": "object", + "properties": { + "application_ids": { + "type": "object" + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN DevEUI." + }, + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "The LoRaWAN DevAddr." + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the end device fields that should be returned.\nSee the API reference for which fields can be returned by the different services." + } + } + } } ], "tags": [ - "UserRegistry" + "NsEndDeviceRegistry" ] } }, - "/users/{user_id}": { - "delete": { - "operationId": "UserRegistry_Delete", + "/ns/default_mac_settings/{frequency_plan_id}/{lorawan_phy_version}": { + "get": { + "summary": "GetDefaultMACSettings retrieves the default MAC settings for a frequency plan.", + "operationId": "Ns_GetDefaultMACSettings", "responses": { "200": { "description": "A successful response.", "schema": { - "properties": {} + "$ref": "#/definitions/v3MACSettings" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, "parameters": [ { - "name": "user_id", - "description": "This ID shares namespace with organization IDs.", + "name": "frequency_plan_id", "in": "path", "required": true, "type": "string" }, { - "name": "email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" + "name": "lorawan_phy_version", + "in": "path", + "required": true, + "type": "string", + "enum": [ + "PHY_UNKNOWN", + "PHY_V1_0", + "TS001_V1_0", + "PHY_V1_0_1", + "TS001_V1_0_1", + "PHY_V1_0_2_REV_A", + "RP001_V1_0_2", + "PHY_V1_0_2_REV_B", + "RP001_V1_0_2_REV_B", + "PHY_V1_1_REV_A", + "RP001_V1_1_REV_A", + "PHY_V1_1_REV_B", + "RP001_V1_1_REV_B", + "PHY_V1_0_3_REV_A", + "RP001_V1_0_3_REV_A", + "RP002_V1_0_0", + "RP002_V1_0_1", + "RP002_V1_0_2", + "RP002_V1_0_3" + ] } ], "tags": [ - "UserRegistry" + "Ns" ] } }, - "/users/{user_id}/rights": { + "/ns/dev_addr": { "get": { - "operationId": "UserAccess_ListRights", + "summary": "GenerateDevAddr requests a device address assignment from the Network Server.", + "operationId": "Ns_GenerateDevAddr", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/v3Rights" + "$ref": "#/definitions/v3GenerateDevAddrResponse" } }, "default": { - "description": "An unexpected error response", + "description": "An unexpected error response.", "schema": { - "$ref": "#/definitions/runtimeError" + "$ref": "#/definitions/googlerpcStatus" } } }, - "parameters": [ - { - "name": "user_id", - "description": "This ID shares namespace with organization IDs.", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "email", - "description": "Secondary identifier, which can only be used in specific requests.", - "in": "query", - "required": false, - "type": "string" - } - ], "tags": [ - "UserAccess" + "Ns" ] } - } - }, - "definitions": { + }, + "/ns/dev_addr_prefixes": { + "get": { + "operationId": "Ns_GetDeviceAddressPrefixes", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetDeviceAdressPrefixesResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Ns" + ] + } + }, + "/ns/net_id": { + "get": { + "operationId": "Ns_GetNetID", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetNetIDResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Ns" + ] + } + }, + "/organizations": { + "get": { + "summary": "List organizations where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the organizations the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "OrganizationRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organizations" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the organization fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted organizations.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{collaborator.organization_ids.organization_id}/applications": { + "get": { + "summary": "List applications where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the applications the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ApplicationRegistry_List3", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Applications" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the application fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted applications.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "ApplicationRegistry" + ] + }, + "post": { + "summary": "Create a new application. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "ApplicationRegistry_Create2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Application" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/v3Application" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "user_ids": { + "$ref": "#/definitions/v3UserIdentifiers" + } + }, + "description": "Collaborator to grant all rights on the newly created application.", + "title": "Collaborator to grant all rights on the newly created application." + } + } + } + } + ], + "tags": [ + "ApplicationRegistry" + ] + } + }, + "/organizations/{collaborator.organization_ids.organization_id}/clients": { + "get": { + "summary": "List OAuth clients where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the OAuth clients the caller\nhas access to.\nSimilar to Get, this selects the fields specified in the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ClientRegistry_List3", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Clients" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the client fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted clients.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "ClientRegistry" + ] + }, + "post": { + "summary": "Create a new OAuth client. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "ClientRegistry_Create2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Client" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "client": { + "$ref": "#/definitions/v3Client" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "user_ids": { + "$ref": "#/definitions/v3UserIdentifiers" + } + }, + "description": "Collaborator to grant all rights on the newly created client.", + "title": "Collaborator to grant all rights on the newly created client." + } + } + } + } + ], + "tags": [ + "ClientRegistry" + ] + } + }, + "/organizations/{collaborator.organization_ids.organization_id}/gateways": { + "get": { + "summary": "List gateways where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the gateways the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "GatewayRegistry_List3", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Gateways" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the gateway fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted gateways.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "GatewayRegistry" + ] + }, + "post": { + "summary": "Create a new gateway. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "GatewayRegistry_Create2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Gateway" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "gateway": { + "$ref": "#/definitions/v3Gateway" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "user_ids": { + "$ref": "#/definitions/v3UserIdentifiers" + } + }, + "description": "Collaborator to grant all rights on the newly created gateway.", + "title": "Collaborator to grant all rights on the newly created gateway." + } + } + } + } + ], + "tags": [ + "GatewayRegistry" + ] + } + }, + "/organizations/{organization.ids.organization_id}": { + "put": { + "summary": "Update the organization, changing the fields specified by the field mask to the provided values.", + "operationId": "OrganizationRegistry_Update", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organization" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization.ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "organization": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "description": "The identifiers of the organization. These are public and can be seen by any authenticated user in the network.", + "title": "The identifiers of the organization. These are public and can be seen by any authenticated user in the network." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the organization was created. This information is public and can be seen by any authenticated user in the network." + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the organization was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the organization was deleted. This information is public and can be seen by any authenticated user in the network." + }, + "name": { + "type": "string", + "description": "The name of the organization. This information is public and can be seen by any authenticated user in the network." + }, + "description": { + "type": "string", + "description": "A description for the organization." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this organization. Typically used for organizing organizations or for storing integration-specific data." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the organization fields that should be updated." + } + } + } + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{organization_ids.organization_id}": { + "get": { + "summary": "Get the organization with the given identifiers, selecting the fields specified\nin the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "OrganizationRegistry_Get", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organization" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the organization fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{organization_ids.organization_id}/api-keys": { + "get": { + "summary": "List the API keys for this organization.", + "operationId": "OrganizationAccess_ListAPIKeys", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKeys" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path.\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "OrganizationAccess" + ] + }, + "post": { + "summary": "Create an API key scoped to this organization.\nOrganization API keys can give access to the organization itself, as well as\nany application, gateway and OAuth client this organization is a collaborator of.", + "operationId": "OrganizationAccess_CreateAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "name": { + "type": "string" + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + } + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + } + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/organizations/{organization_ids.organization_id}/api-keys/{api_key.id}": { + "put": { + "summary": "Update the rights of an API key of the organization.\nThis method can also be used to delete the API key, by giving it no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "OrganizationAccess_UpdateAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "api_key.id", + "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "api_key": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Immutable and unique secret value of the API key.\nGenerated by the Access Server." + }, + "name": { + "type": "string", + "description": "User-defined (friendly) name for the API key." + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Rights that are granted to this API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the api key fields that should be updated." + } + } + } + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/organizations/{organization_ids.organization_id}/api-keys/{key_id}": { + "get": { + "summary": "Get a single API key of this organization.", + "operationId": "OrganizationAccess_GetAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "key_id", + "description": "Unique public identifier for the API key.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/organizations/{organization_ids.organization_id}/collaborator/user/{collaborator.user_ids.user_id}": { + "get": { + "summary": "Get the rights of a collaborator (member) of the organization.\nPseudo-rights in the response (such as the \"_ALL\" right) are not expanded.", + "operationId": "OrganizationAccess_GetCollaborator", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GetCollaboratorResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/organizations/{organization_ids.organization_id}/collaborators": { + "get": { + "summary": "List the collaborators on this organization.", + "operationId": "OrganizationAccess_ListCollaborators", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Collaborators" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "OrganizationAccess" + ] + }, + "put": { + "summary": "Set the rights of a collaborator (member) on the organization.\nOrganization collaborators can get access to the organization itself, as well as\nany application, gateway and OAuth client this organization is a collaborator of.\nThis method can also be used to delete the collaborator, by giving them no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "OrganizationAccess_SetCollaborator", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "organization_ids": { + "type": "object" + }, + "collaborator": { + "$ref": "#/definitions/v3Collaborator" + } + } + } + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/organizations/{organization_ids.organization_id}/collaborators/search": { + "get": { + "summary": "Search for accounts that match the conditions specified in the request.", + "operationId": "EntityRegistrySearch_SearchAccounts5", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3SearchAccountsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "query", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "only_users", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "application_ids.application_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "client_ids.client_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.gateway_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/organizations/{organization_id}": { + "delete": { + "summary": "Delete the organization. This may not release the organization ID for reuse.", + "operationId": "OrganizationRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{organization_id}/purge": { + "delete": { + "summary": "Purge the organization. This will release the organization ID for reuse.\nThe user is responsible for clearing data from any (external) integrations\nthat may store and expose data by user or organization ID.", + "operationId": "OrganizationRegistry_Purge", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{organization_id}/restore": { + "post": { + "summary": "Restore a recently deleted organization.", + "description": "Deployment configuration may specify if, and for how long after deletion,\nentities can be restored.", + "operationId": "OrganizationRegistry_Restore", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/organizations/{organization_id}/rights": { + "get": { + "summary": "List the rights the caller has on this organization.", + "operationId": "OrganizationAccess_ListRights", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Rights" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "OrganizationAccess" + ] + } + }, + "/pba/forwarders/policies": { + "get": { + "summary": "List the routing policies that Forwarders configured with Packet Broker Agent as Home Network.\nListing routing policies requires administrative access.", + "operationId": "Pba_ListForwarderRoutingPolicies", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicies" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "home_network_id.net_id", + "description": "LoRa Alliance NetID.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "home_network_id.tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks": { + "get": { + "summary": "List the listed home networks for which routing policies can be configured.\nListing home networks requires administrative access.", + "operationId": "Pba_ListHomeNetworks", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerNetworks" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "tenant_id_contains", + "description": "Filter by tenant ID.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Filter by name.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/gateway-visibilities/default": { + "get": { + "summary": "Get the default gateway visibility.\nGetting gateway visibilities requires administrative access.", + "operationId": "Pba_GetHomeNetworkDefaultGatewayVisibility", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerDefaultGatewayVisibility" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + }, + "delete": { + "summary": "Deletes the default gateway visibility.\nDeleting gateway visibilities requires administrative access.", + "operationId": "Pba_DeleteHomeNetworkDefaultGatewayVisibility", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + }, + "post": { + "summary": "Set the default gateway visibility.\nSetting gateway visibilities requires administrative access.", + "operationId": "Pba_SetHomeNetworkDefaultGatewayVisibility2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3SetPacketBrokerDefaultGatewayVisibilityRequest" + } + } + ], + "tags": [ + "Pba" + ] + }, + "put": { + "summary": "Set the default gateway visibility.\nSetting gateway visibilities requires administrative access.", + "operationId": "Pba_SetHomeNetworkDefaultGatewayVisibility", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3SetPacketBrokerDefaultGatewayVisibilityRequest" + } + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies": { + "get": { + "summary": "List the routing policies that Packet Broker Agent as Forwarder configured with Home Networks.\nListing routing policies requires administrative access.", + "operationId": "Pba_ListHomeNetworkRoutingPolicies", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicies" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies/default": { + "get": { + "summary": "Get the default routing policy.\nGetting routing policies requires administrative access.", + "operationId": "Pba_GetHomeNetworkDefaultRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerDefaultRoutingPolicy" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + }, + "delete": { + "summary": "Deletes the default routing policy.\nDeleting routing policies requires administrative access.", + "operationId": "Pba_DeleteHomeNetworkDefaultRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + }, + "post": { + "summary": "Set the default routing policy.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkDefaultRoutingPolicy2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3SetPacketBrokerDefaultRoutingPolicyRequest" + } + } + ], + "tags": [ + "Pba" + ] + }, + "put": { + "summary": "Set the default routing policy.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkDefaultRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3SetPacketBrokerDefaultRoutingPolicyRequest" + } + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies/{home_network_id.net_id}": { + "post": { + "summary": "Set the routing policy for the given Home Network.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkRoutingPolicy2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "home_network_id.net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "home_network_id": { + "type": "object", + "properties": { + "tenant_id": { + "type": "string", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID." + } + }, + "description": "Packet Broker identifier of the Home Network.", + "title": "Packet Broker identifier of the Home Network." + }, + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + } + } + ], + "tags": [ + "Pba" + ] + }, + "put": { + "summary": "Set the routing policy for the given Home Network.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "home_network_id.net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "home_network_id": { + "type": "object", + "properties": { + "tenant_id": { + "type": "string", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID." + } + }, + "description": "Packet Broker identifier of the Home Network.", + "title": "Packet Broker identifier of the Home Network." + }, + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + } + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies/{home_network_id.net_id}/{home_network_id.tenant_id}": { + "post": { + "summary": "Set the routing policy for the given Home Network.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkRoutingPolicy4", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "home_network_id.net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "home_network_id.tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "home_network_id": { + "type": "object", + "description": "Packet Broker identifier of the Home Network.", + "title": "Packet Broker identifier of the Home Network." + }, + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + } + } + ], + "tags": [ + "Pba" + ] + }, + "put": { + "summary": "Set the routing policy for the given Home Network.\nSetting routing policies requires administrative access.", + "operationId": "Pba_SetHomeNetworkRoutingPolicy3", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "home_network_id.net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "home_network_id.tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "home_network_id": { + "type": "object", + "description": "Packet Broker identifier of the Home Network.", + "title": "Packet Broker identifier of the Home Network." + }, + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + } + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies/{net_id}": { + "get": { + "summary": "Get the routing policy for the given Home Network.\nGetting routing policies requires administrative access.", + "operationId": "Pba_GetHomeNetworkRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicy" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + }, + "delete": { + "summary": "Delete the routing policy for the given Home Network.\nDeleting routing policies requires administrative access.", + "operationId": "Pba_DeleteHomeNetworkRoutingPolicy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/home-networks/policies/{net_id}/{tenant_id}": { + "get": { + "summary": "Get the routing policy for the given Home Network.\nGetting routing policies requires administrative access.", + "operationId": "Pba_GetHomeNetworkRoutingPolicy2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicy" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + }, + "delete": { + "summary": "Delete the routing policy for the given Home Network.\nDeleting routing policies requires administrative access.", + "operationId": "Pba_DeleteHomeNetworkRoutingPolicy2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "net_id", + "description": "LoRa Alliance NetID.", + "in": "path", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "tenant_id", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/info": { + "get": { + "summary": "Get information about the Packet Broker registration.\nViewing Packet Packet information requires administrative access.", + "operationId": "Pba_GetInfo", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerInfo" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + } + }, + "/pba/networks": { + "get": { + "summary": "List all listed networks.\nListing networks requires administrative access.", + "operationId": "Pba_ListNetworks", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerNetworks" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "with_routing_policy", + "description": "If true, list only the Forwarders and Home Networks with whom a routing policy has been defined in either direction.", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "tenant_id_contains", + "description": "Filter by tenant ID.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Filter by name.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Pba" + ] + } + }, + "/pba/registration": { + "delete": { + "summary": "Deregister from Packet Broker.\nPacket Broker deregistration requires administrative access.\nPacket Broker deregistration is only supported for tenants and requires Packet Broker Agent to be configured with\nNetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled.", + "operationId": "Pba_Deregister", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "Pba" + ] + }, + "post": { + "summary": "Register with Packet Broker. If no registration exists, it will be created. Any existing registration will be updated.\nRegistration settings not in the request message are taken from Packet Broker Agent configuration and caller context.\nPacket Broker registration requires administrative access.\nPacket Broker registration is only supported for tenants and requires Packet Broker Agent to be configured with\nNetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled.", + "operationId": "Pba_Register2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerNetwork" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3PacketBrokerRegisterRequest" + } + } + ], + "tags": [ + "Pba" + ] + }, + "put": { + "summary": "Register with Packet Broker. If no registration exists, it will be created. Any existing registration will be updated.\nRegistration settings not in the request message are taken from Packet Broker Agent configuration and caller context.\nPacket Broker registration requires administrative access.\nPacket Broker registration is only supported for tenants and requires Packet Broker Agent to be configured with\nNetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled.", + "operationId": "Pba_Register", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3PacketBrokerNetwork" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3PacketBrokerRegisterRequest" + } + } + ], + "tags": [ + "Pba" + ] + } + }, + "/qr-code/end-devices/parse": { + "post": { + "summary": "Parse QR Codes of known formats and return the information contained within.", + "operationId": "EndDeviceQRCodeGenerator_Parse", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ParseEndDeviceQRCodeResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3ParseEndDeviceQRCodeRequest" + } + } + ], + "tags": [ + "EndDeviceQRCodeGenerator" + ] + } + }, + "/qr-code/end-devices/{format_id}/parse": { + "post": { + "summary": "Parse QR Codes of known formats and return the information contained within.", + "operationId": "EndDeviceQRCodeGenerator_Parse2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ParseEndDeviceQRCodeResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "format_id", + "description": "QR code format identifier.\nEnumerate available formats with the rpc `ListFormats`.\nIf this field is not specified, the server will attempt to parse the data with each known format.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "qr_code": { + "type": "string", + "format": "byte", + "description": "Raw QR code contents." + } + } + } + } + ], + "tags": [ + "EndDeviceQRCodeGenerator" + ] + } + }, + "/qr-codes/end-devices": { + "post": { + "summary": "Generates a QR code.", + "operationId": "EndDeviceQRCodeGenerator_Generate", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3GenerateQRCodeResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3GenerateEndDeviceQRCodeRequest" + } + } + ], + "tags": [ + "EndDeviceQRCodeGenerator" + ] + } + }, + "/qr-codes/end-devices/formats": { + "get": { + "summary": "Returns the supported formats.", + "operationId": "EndDeviceQRCodeGenerator_ListFormats", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3QRCodeFormats" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "tags": [ + "EndDeviceQRCodeGenerator" + ] + } + }, + "/qr-codes/end-devices/formats/{format_id}": { + "get": { + "summary": "Return the QR code format.", + "operationId": "EndDeviceQRCodeGenerator_GetFormat", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3QRCodeFormat" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "format_id", + "description": "QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service.", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "EndDeviceQRCodeGenerator" + ] + } + }, + "/search/accounts": { + "get": { + "summary": "Search for accounts that match the conditions specified in the request.", + "operationId": "EntityRegistrySearch_SearchAccounts", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3SearchAccountsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "only_users", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "application_ids.application_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "client_ids.client_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.gateway_id", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "gateway_ids.eui", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string", + "format": "string" + }, + { + "name": "organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/search/applications": { + "get": { + "summary": "Search for applications that match the conditions specified in the request.\nNon-admin users will only match applications that they have rights on.", + "operationId": "EntityRegistrySearch_SearchApplications", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Applications" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "description": "Find applications where the ID, name or description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find applications where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find applications where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find applications where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted applications.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/search/applications/{application_ids.application_id}/devices": { + "get": { + "summary": "Search for end devices in the given application that match the conditions specified in the request.", + "operationId": "EndDeviceRegistrySearch_SearchEndDevices", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3EndDevices" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "application_ids.application_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "query", + "description": "Find end devices where the ID, name, description or EUI contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find end devices where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find end devices where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find end devices where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "dev_eui_contains", + "description": "Find end devices where the (hexadecimal) DevEUI contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "join_eui_contains", + "description": "Find end devices where the (hexadecimal) JoinEUI contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "dev_addr_contains", + "description": "Find end devices where the (hexadecimal) DevAddr contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "EndDeviceRegistrySearch" + ] + } + }, + "/search/clients": { + "get": { + "summary": "Search for OAuth clients that match the conditions specified in the request.\nNon-admin users will only match OAuth clients that they have rights on.", + "operationId": "EntityRegistrySearch_SearchClients", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Clients" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "description": "Find OAuth clients where the ID, name or description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find OAuth clients where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find OAuth clients where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find OAuth clients where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "state", + "description": "Find OAuth clients where the state is any of these states.\n\n - STATE_REQUESTED: Denotes that the entity has been requested and is pending review by an admin.\n - STATE_APPROVED: Denotes that the entity has been reviewed and approved by an admin.\n - STATE_REJECTED: Denotes that the entity has been reviewed and rejected by an admin.\n - STATE_FLAGGED: Denotes that the entity has been flagged and is pending review by an admin.\n - STATE_SUSPENDED: Denotes that the entity has been reviewed and suspended by an admin.", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "STATE_REQUESTED", + "STATE_APPROVED", + "STATE_REJECTED", + "STATE_FLAGGED", + "STATE_SUSPENDED" + ] + }, + "collectionFormat": "multi" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted OAuth clients.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/search/gateways": { + "get": { + "summary": "Search for gateways that match the conditions specified in the request.\nNon-admin users will only match gateways that they have rights on.", + "operationId": "EntityRegistrySearch_SearchGateways", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Gateways" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "description": "Find gateways where the ID, name, description or EUI contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find gateways where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find gateways where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find gateways where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "eui_contains", + "description": "Find gateways where the (hexadecimal) EUI contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted gateways.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/search/organizations": { + "get": { + "summary": "Search for organizations that match the conditions specified in the request.\nNon-admin users will only match organizations that they have rights on.", + "operationId": "EntityRegistrySearch_SearchOrganizations", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organizations" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "description": "Find organizations where the ID, name or description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find organizations where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find organizations where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find organizations where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted organizations.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/search/users": { + "get": { + "summary": "Search for users that match the conditions specified in the request.\nThis is only available to admin users.", + "operationId": "EntityRegistrySearch_SearchUsers", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Users" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "query", + "description": "Find users where the ID, name or description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "id_contains", + "description": "Find users where the ID contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "name_contains", + "description": "Find users where the name contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "description_contains", + "description": "Find users where the description contains this substring.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "state", + "description": "Find users where the state is any of these states.\n\n - STATE_REQUESTED: Denotes that the entity has been requested and is pending review by an admin.\n - STATE_APPROVED: Denotes that the entity has been reviewed and approved by an admin.\n - STATE_REJECTED: Denotes that the entity has been reviewed and rejected by an admin.\n - STATE_FLAGGED: Denotes that the entity has been flagged and is pending review by an admin.\n - STATE_SUSPENDED: Denotes that the entity has been reviewed and suspended by an admin.", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "STATE_REQUESTED", + "STATE_APPROVED", + "STATE_REJECTED", + "STATE_FLAGGED", + "STATE_SUSPENDED" + ] + }, + "collectionFormat": "multi" + }, + { + "name": "field_mask", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted users.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "EntityRegistrySearch" + ] + } + }, + "/users": { + "get": { + "summary": "List users of the network. This method is typically restricted to admins only.", + "operationId": "UserRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Users" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "field_mask", + "description": "The names of the user fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted users.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "UserRegistry" + ] + }, + "post": { + "summary": "Register a new user. This method may be restricted by network settings.", + "operationId": "UserRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3User" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v3CreateUserRequest" + } + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{collaborator.user_ids.user_id}/applications": { + "get": { + "summary": "List applications where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the applications the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ApplicationRegistry_List2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Applications" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the application fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted applications.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "ApplicationRegistry" + ] + }, + "post": { + "summary": "Create a new application. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "ApplicationRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Application" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "application": { + "$ref": "#/definitions/v3Application" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "$ref": "#/definitions/v3OrganizationIdentifiers" + }, + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + } + }, + "description": "Collaborator to grant all rights on the newly created application.", + "title": "Collaborator to grant all rights on the newly created application." + } + } + } + } + ], + "tags": [ + "ApplicationRegistry" + ] + } + }, + "/users/{collaborator.user_ids.user_id}/clients": { + "get": { + "summary": "List OAuth clients where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the OAuth clients the caller\nhas access to.\nSimilar to Get, this selects the fields specified in the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "ClientRegistry_List2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Clients" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the client fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted clients.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "ClientRegistry" + ] + }, + "post": { + "summary": "Create a new OAuth client. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "ClientRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Client" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "client": { + "$ref": "#/definitions/v3Client" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "$ref": "#/definitions/v3OrganizationIdentifiers" + }, + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + } + }, + "description": "Collaborator to grant all rights on the newly created client.", + "title": "Collaborator to grant all rights on the newly created client." + } + } + } + } + ], + "tags": [ + "ClientRegistry" + ] + } + }, + "/users/{collaborator.user_ids.user_id}/gateways": { + "get": { + "summary": "List gateways where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the gateways the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "GatewayRegistry_List2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Gateways" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the gateway fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted gateways.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "GatewayRegistry" + ] + }, + "post": { + "summary": "Create a new gateway. This also sets the given organization or user as\nfirst collaborator with all possible rights.", + "operationId": "GatewayRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Gateway" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "gateway": { + "$ref": "#/definitions/v3Gateway" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "$ref": "#/definitions/v3OrganizationIdentifiers" + }, + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + } + }, + "description": "Collaborator to grant all rights on the newly created gateway.", + "title": "Collaborator to grant all rights on the newly created gateway." + } + } + } + } + ], + "tags": [ + "GatewayRegistry" + ] + } + }, + "/users/{collaborator.user_ids.user_id}/organizations": { + "get": { + "summary": "List organizations where the given user or organization is a direct collaborator.\nIf no user or organization is given, this returns the organizations the caller\nhas access to.\nSimilar to Get, this selects the fields given by the field mask.\nMore or less fields may be returned, depending on the rights of the caller.", + "operationId": "OrganizationRegistry_List2", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organizations" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "collaborator.organization_ids.organization_id", + "description": "This ID shares namespace with user IDs.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "collaborator.user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the organization fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "deleted", + "description": "Only return recently deleted organizations.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "OrganizationRegistry" + ] + }, + "post": { + "summary": "Create a new organization. This also sets the given user as\nfirst collaborator with all possible rights.", + "operationId": "OrganizationRegistry_Create", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Organization" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "collaborator.user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "organization": { + "$ref": "#/definitions/v3Organization" + }, + "collaborator": { + "type": "object", + "properties": { + "organization_ids": { + "$ref": "#/definitions/v3OrganizationIdentifiers" + }, + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + } + }, + "description": "Collaborator to grant all rights on the newly created application.\nNOTE: It is currently not possible to have organizations collaborating on\nother organizations.", + "title": "Collaborator to grant all rights on the newly created application.\nNOTE: It is currently not possible to have organizations collaborating on\nother organizations." + } + } + } + } + ], + "tags": [ + "OrganizationRegistry" + ] + } + }, + "/users/{receiver_ids.user_id}/notifications": { + "get": { + "summary": "List the notifications for a user or an organization.\nWhen called with user credentials and empty receiver_ids, this will list\nnotifications for the current user and its organizations.", + "operationId": "NotificationService_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3ListNotificationsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "receiver_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "receiver_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "status", + "description": "Select notifications with these statuses.\nAn empty list is interpreted as \"all\".", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "NOTIFICATION_STATUS_UNSEEN", + "NOTIFICATION_STATUS_SEEN", + "NOTIFICATION_STATUS_ARCHIVED" + ] + }, + "collectionFormat": "multi" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "NotificationService" + ] + }, + "patch": { + "summary": "Batch-update multiple notifications to the same status.", + "operationId": "NotificationService_UpdateStatus", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "receiver_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "receiver_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + }, + "description": "The IDs of the receiving user.", + "title": "The IDs of the receiving user." + }, + "ids": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The IDs of the notifications to update the status of." + }, + "status": { + "$ref": "#/definitions/v3NotificationStatus", + "description": "The status to set on the notifications." + } + } + } + } + ], + "tags": [ + "NotificationService" + ] + } + }, + "/users/{user.ids.user_id}": { + "put": { + "summary": "Update the user, changing the fields specified by the field mask to the provided values.\nThis method can not be used to change the password, see the UpdatePassword method for that.", + "operationId": "UserRegistry_Update", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3User" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user.ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "user": { + "type": "object", + "properties": { + "ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + }, + "description": "The identifiers of the user. These are public and can be seen by any authenticated user in the network.", + "title": "The identifiers of the user. These are public and can be seen by any authenticated user in the network." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the user was created. This information is public and can be seen by any authenticated user in the network." + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "When the user was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the user was deleted. This information is public and can be seen by any authenticated user in the network." + }, + "name": { + "type": "string", + "description": "The name of the user. This information is public and can be seen by any authenticated user in the network." + }, + "description": { + "type": "string", + "description": "A description for the user. This information is public and can be seen by any authenticated user in the network." + }, + "attributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Key-value attributes for this users. Typically used for storing integration-specific data." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user.\nThis field is deprecated." + }, + "primary_email_address": { + "type": "string", + "description": "Primary email address that can be used for logging in.\nThis address is not public, use contact_info for that." + }, + "primary_email_address_validated_at": { + "type": "string", + "format": "date-time", + "description": "When the primary email address was validated. Note that email address validation is not required on all networks." + }, + "password": { + "type": "string", + "description": "The password field is only considered when creating a user.\nIt is not returned on API calls, and can not be updated by updating the User.\nSee the UpdatePassword method of the UserRegistry service for more information." + }, + "password_updated_at": { + "type": "string", + "format": "date-time" + }, + "require_password_update": { + "type": "boolean" + }, + "state": { + "$ref": "#/definitions/v3State", + "description": "The reviewing state of the user.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." + }, + "state_description": { + "type": "string", + "description": "A description for the state field.\nThis field can only be modified by admins, and should typically only be updated\nwhen also updating `state`." + }, + "admin": { + "type": "boolean", + "description": "This user is an admin.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by other admins." + }, + "temporary_password": { + "type": "string", + "description": "The temporary password can only be used to update a user's password; never returned on API calls.\nIt is not returned on API calls, and can not be updated by updating the User.\nSee the CreateTemporaryPassword method of the UserRegistry service for more information." + }, + "temporary_password_created_at": { + "type": "string", + "format": "date-time" + }, + "temporary_password_expires_at": { + "type": "string", + "format": "date-time" + }, + "profile_picture": { + "$ref": "#/definitions/v3Picture", + "description": "A profile picture for the user.\nThis information is public and can be seen by any authenticated user in the network." + } + }, + "description": "User is the message that defines a user on the network." + }, + "field_mask": { + "type": "string", + "description": "The names of the user fields that should be updated." + } + } + } + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_ids.user_id}": { + "get": { + "summary": "Get the user with the given identifiers, selecting the fields given by the\nfield mask. The method may return more or less fields, depending on the rights\nof the caller.", + "operationId": "UserRegistry_Get", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3User" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "field_mask", + "description": "The names of the user fields that should be returned.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_ids.user_id}/api-keys": { + "get": { + "summary": "List the API keys for this user.", + "operationId": "UserAccess_ListAPIKeys", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKeys" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path.\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "UserAccess" + ] + }, + "post": { + "summary": "Create an API key scoped to this user.\nUser API keys can give access to the user itself, as well as\nany organization, application, gateway and OAuth client this user is a collaborator of.", + "operationId": "UserAccess_CreateAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "name": { + "type": "string" + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + } + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + } + } + ], + "tags": [ + "UserAccess" + ] + } + }, + "/users/{user_ids.user_id}/api-keys/{api_key.id}": { + "put": { + "summary": "Update the rights of an API key of the user.\nThis method can also be used to delete the API key, by giving it no rights.\nThe caller is required to have all assigned or/and removed rights.", + "operationId": "UserAccess_UpdateAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "api_key.id", + "description": "Immutable and unique public identifier for the API key.\nGenerated by the Access Server.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "api_key": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Immutable and unique secret value of the API key.\nGenerated by the Access Server." + }, + "name": { + "type": "string", + "description": "User-defined (friendly) name for the API key." + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + }, + "description": "Rights that are granted to this API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + } + }, + "field_mask": { + "type": "string", + "description": "The names of the api key fields that should be updated." + } + } + } + } + ], + "tags": [ + "UserAccess" + ] + } + }, + "/users/{user_ids.user_id}/api-keys/{key_id}": { + "get": { + "summary": "Get a single API key of this user.", + "operationId": "UserAccess_GetAPIKey", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3APIKey" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "key_id", + "description": "Unique public identifier for the API key.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserAccess" + ] + } + }, + "/users/{user_ids.user_id}/authorizations": { + "get": { + "summary": "List OAuth clients that are authorized by the user.", + "operationId": "OAuthAuthorizationRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3OAuthClientAuthorizations" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "OAuthAuthorizationRegistry" + ] + } + }, + "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}": { + "delete": { + "summary": "Delete (de-authorize) an OAuth client for the user.", + "operationId": "OAuthAuthorizationRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "client_ids.client_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "OAuthAuthorizationRegistry" + ] + } + }, + "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens": { + "get": { + "summary": "List OAuth access tokens issued to the OAuth client on behalf of the user.", + "operationId": "OAuthAuthorizationRegistry_ListTokens", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3OAuthAccessTokens" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "client_ids.client_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "OAuthAuthorizationRegistry" + ] + } + }, + "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens/{id}": { + "delete": { + "summary": "Delete (invalidate) an OAuth access token.", + "operationId": "OAuthAuthorizationRegistry_DeleteToken", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "client_ids.client_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "OAuthAuthorizationRegistry" + ] + } + }, + "/users/{user_ids.user_id}/login-tokens": { + "post": { + "summary": "Create a login token that can be used for a one-time login as a user.", + "operationId": "UserAccess_CreateLoginToken", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3CreateLoginTokenResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "skip_email", + "description": "Skip sending the login token to the user by email.\nThis field is only effective when the login token is created by an admin user.", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "UserAccess" + ] + } + }, + "/users/{user_ids.user_id}/password": { + "put": { + "summary": "Update the password of the user.", + "operationId": "UserRegistry_UpdatePassword", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "properties": { + "user_ids": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "Secondary identifier, which can only be used in specific requests." + } + } + }, + "new": { + "type": "string" + }, + "old": { + "type": "string" + }, + "revoke_all_access": { + "type": "boolean", + "description": "Revoke active sessions and access tokens of user if true. To be used if credentials are suspected to be compromised." + } + } + } + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_ids.user_id}/sessions": { + "get": { + "summary": "List the active sessions for the given user.", + "operationId": "UserSessionRegistry_List", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3UserSessions" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "order", + "description": "Order the results by this field path (must be present in the field mask).\nDefault ordering is by ID. Prepend with a minus (-) to reverse the order.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "limit", + "description": "Limit the number of results per page.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "page", + "description": "Page number for pagination. 0 is interpreted as 1.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + } + ], + "tags": [ + "UserSessionRegistry" + ] + } + }, + "/users/{user_ids.user_id}/sessions/{session_id}": { + "delete": { + "summary": "Delete (revoke) the given user session.", + "operationId": "UserSessionRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "session_id", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserSessionRegistry" + ] + } + }, + "/users/{user_ids.user_id}/temporary_password": { + "post": { + "summary": "Create a temporary password that can be used for updating a forgotten password.\nThe generated password is sent to the user's email address.", + "operationId": "UserRegistry_CreateTemporaryPassword", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_ids.user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "user_ids.email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_id}": { + "delete": { + "summary": "Delete the user. This may not release the user ID for reuse.", + "operationId": "UserRegistry_Delete", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_id}/purge": { + "delete": { + "summary": "Purge the user. This will release the user ID for reuse.\nThe user is responsible for clearing data from any (external) integrations\nthat may store and expose data by user or organization ID.", + "operationId": "UserRegistry_Purge", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_id}/restore": { + "post": { + "summary": "Restore a recently deleted user.", + "description": "Deployment configuration may specify if, and for how long after deletion,\nentities can be restored.", + "operationId": "UserRegistry_Restore", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "type": "object", + "properties": {} + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserRegistry" + ] + } + }, + "/users/{user_id}/rights": { + "get": { + "summary": "List the rights the caller has on this user.", + "operationId": "UserAccess_ListRights", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v3Rights" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/googlerpcStatus" + } + } + }, + "parameters": [ + { + "name": "user_id", + "description": "This ID shares namespace with organization IDs.", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "email", + "description": "Secondary identifier, which can only be used in specific requests.", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "UserAccess" + ] + } + } + }, + "definitions": { + "ADRSettingsDisabledMode": { + "type": "object", + "description": "Configuration options for cases in which ADR is to be disabled\ncompletely." + }, + "ADRSettingsDynamicMode": { + "type": "object", + "properties": { + "margin": { + "type": "number", + "format": "float", + "description": "The ADR margin (dB) tells the network server how much margin it should add in ADR requests.\nA bigger margin is less efficient, but gives a better chance of successful reception.\nIf unset, the default value from Network Server configuration will be used." + }, + "min_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndexValue", + "description": "Minimum data rate index.\nIf unset, the default value from Network Server configuration will be used." + }, + "max_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndexValue", + "description": "Maximum data rate index.\nIf unset, the default value from Network Server configuration will be used." + }, + "min_tx_power_index": { + "type": "integer", + "format": "int64", + "description": "Minimum transmission power index.\nIf unset, the default value from Network Server configuration will be used." + }, + "max_tx_power_index": { + "type": "integer", + "format": "int64", + "description": "Maximum transmission power index.\nIf unset, the default value from Network Server configuration will be used." + }, + "min_nb_trans": { + "type": "integer", + "format": "int64", + "description": "Minimum number of retransmissions.\nIf unset, the default value from Network Server configuration will be used." + }, + "max_nb_trans": { + "type": "integer", + "format": "int64", + "description": "Maximum number of retransmissions.\nIf unset, the default value from Network Server configuration will be used." + } + }, + "description": "Configuration options for dynamic ADR." + }, + "ADRSettingsStaticMode": { + "type": "object", + "properties": { + "data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex", + "description": "Data rate index to use." + }, + "tx_power_index": { + "type": "integer", + "format": "int64", + "description": "Transmission power index to use." + }, + "nb_trans": { + "type": "integer", + "format": "int64", + "description": "Number of retransmissions." + } + }, + "description": "Configuration options for static ADR." + }, + "AWSIoTProviderAccessKey": { + "type": "object", + "properties": { + "access_key_id": { + "type": "string" + }, + "secret_access_key": { + "type": "string" + }, + "session_token": { + "type": "string" + } + } + }, + "AWSIoTProviderAssumeRole": { + "type": "object", + "properties": { + "arn": { + "type": "string" + }, + "external_id": { + "type": "string" + }, + "session_duration": { + "type": "string" + } + } + }, + "AWSIoTProviderDefaultIntegration": { + "type": "object", + "properties": { + "stack_name": { + "type": "string", + "description": "The stack name that is associated with the CloudFormation deployment of The Things Stack Enterprise integration." + } + } + }, "ApplicationDownlinkClassBC": { "type": "object", "properties": { "gateways": { "type": "array", "items": { - "$ref": "#/definitions/v3GatewayAntennaIdentifiers" + "$ref": "#/definitions/v3ClassBCGatewayIdentifiers" }, - "description": "Possible gateway identifiers and antenna index to use for this downlink message.\nThe Network Server selects one of these gateways for downlink, based on connectivity, signal quality, channel utilization and an available slot.\nIf none of the gateways can be selected, the downlink message fails.\nIf empty, a gateway and antenna is selected automatically from the gateways seen in recent uplinks." + "description": "Possible gateway identifiers, antenna index, and group index to use for this downlink message.\nThe Network Server selects one of these gateways for downlink, based on connectivity, signal quality, channel utilization and an available slot.\nIf none of the gateways can be selected, the downlink message fails.\nIf empty, a gateway and antenna is selected automatically from the gateways seen in recent uplinks.\nIf group index is set, gateways will be grouped by the index for the Network Server to select one gateway per group." }, "absolute_time": { "type": "string", @@ -7357,6 +16850,31 @@ } } }, + "ApplicationPubSubAWSIoTProvider": { + "type": "object", + "properties": { + "region": { + "type": "string", + "description": "The AWS region." + }, + "access_key": { + "$ref": "#/definitions/AWSIoTProviderAccessKey", + "description": "If set, the integration will use an AWS access key." + }, + "assume_role": { + "$ref": "#/definitions/AWSIoTProviderAssumeRole", + "description": "If set, the integration will assume the given role during operation." + }, + "endpoint_address": { + "type": "string", + "description": "The endpoint address to connect to. If the endpoint address is left empty,\nthe integration will try to discover it." + }, + "default": { + "$ref": "#/definitions/AWSIoTProviderDefaultIntegration", + "description": "Enable the default integration. This overrides custom base topic and message topics of the pub/sub integration." + } + } + }, "ApplicationPubSubMQTTProvider": { "type": "object", "properties": { @@ -7379,8 +16897,7 @@ "$ref": "#/definitions/MQTTProviderQoS" }, "use_tls": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "tls_ca": { "type": "string", @@ -7392,47 +16909,205 @@ "format": "byte", "description": "The client certificate. PEM formatted." }, - "tls_client_key": { + "tls_client_key": { + "type": "string", + "format": "byte", + "description": "The client private key. PEM formatted." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP headers to use on MQTT-over-Websocket connections." + } + }, + "description": "The MQTT provider settings." + }, + "ApplicationPubSubNATSProvider": { + "type": "object", + "properties": { + "server_url": { + "type": "string", + "description": "The server connection URL." + } + }, + "description": "The NATS provider settings." + }, + "ApplicationWebhookHealthWebhookHealthStatusHealthy": { + "type": "object" + }, + "ApplicationWebhookHealthWebhookHealthStatusUnhealthy": { + "type": "object", + "properties": { + "failed_attempts": { + "type": "string", + "format": "uint64" + }, + "last_failed_attempt_at": { + "type": "string", + "format": "date-time" + }, + "last_failed_attempt_details": { + "$ref": "#/definitions/v3ErrorDetails" + } + } + }, + "AsConfigurationPubSub": { + "type": "object", + "properties": { + "providers": { + "$ref": "#/definitions/PubSubProviders" + } + } + }, + "AsConfigurationWebhooks": { + "type": "object", + "properties": { + "unhealthy_attempts_threshold": { + "type": "string", + "format": "int64" + }, + "unhealthy_retry_interval": { + "type": "string" + } + } + }, + "AuthInfoResponseAPIKeyAccess": { + "type": "object", + "properties": { + "api_key": { + "$ref": "#/definitions/v3APIKey" + }, + "entity_ids": { + "$ref": "#/definitions/v3EntityIdentifiers" + } + } + }, + "AuthInfoResponseGatewayToken": { + "type": "object", + "properties": { + "gateway_ids": { + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" + }, + "rights": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Right" + } + } + } + }, + "BandDescriptionBandDataRate": { + "type": "object", + "properties": { + "rate": { + "$ref": "#/definitions/v3DataRate" + } + } + }, + "BandDescriptionBeacon": { + "type": "object", + "properties": { + "data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "coding_rate": { + "type": "string" + }, + "frequencies": { + "type": "array", + "items": { + "type": "string", + "format": "uint64" + } + } + } + }, + "BandDescriptionDwellTime": { + "type": "object", + "properties": { + "uplinks": { + "type": "boolean" + }, + "downlinks": { + "type": "boolean" + } + } + }, + "BandDescriptionRx2Parameters": { + "type": "object", + "properties": { + "data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "frequency": { "type": "string", - "format": "byte", - "description": "The client private key. PEM formatted." + "format": "uint64" } - }, - "description": "The MQTT provider settings." + } }, - "ApplicationPubSubNATSProvider": { + "BandDescriptionSubBandParameters": { "type": "object", "properties": { - "server_url": { + "min_frequency": { "type": "string", - "description": "The server connection URL." + "format": "uint64" + }, + "max_frequency": { + "type": "string", + "format": "uint64" + }, + "duty_cycle": { + "type": "number", + "format": "float" + }, + "max_eirp": { + "type": "number", + "format": "float" } - }, - "description": "The NATS provider settings." + } }, - "AuthInfoResponseAPIKeyAccess": { + "BatchUpdateEndDeviceLastSeenRequestEndDeviceLastSeenUpdate": { "type": "object", "properties": { - "api_key": { - "$ref": "#/definitions/v3APIKey" + "ids": { + "$ref": "#/definitions/v3EndDeviceIdentifiers" }, - "entity_ids": { - "$ref": "#/definitions/v3EntityIdentifiers" + "last_seen_at": { + "type": "string", + "format": "date-time" } } }, - "ClaimEndDeviceRequestAuthenticatedIdentifiers": { + "CUPSRedirectionClientTLS": { "type": "object", "properties": { - "join_eui": { + "cert": { "type": "string", - "format": "byte" + "format": "byte", + "description": "PEM encoded Client Certificate." }, - "dev_eui": { + "key": { "type": "string", - "format": "byte" + "format": "byte", + "description": "PEM encoded Client Private Key." + } + } + }, + "CompliancesCompliance": { + "type": "object", + "properties": { + "body": { + "type": "string" }, - "authentication_code": { + "norm": { + "type": "string" + }, + "standard": { + "type": "string" + }, + "version": { "type": "string" } } @@ -7492,20 +17167,230 @@ } } }, + "DownlinkMessageMessageMACPayload": { + "type": "object", + "properties": { + "f_port": { + "type": "integer", + "format": "int64" + }, + "full_f_cnt": { + "type": "integer", + "format": "int64" + } + } + }, + "DownlinkMessageMessageMHDR": { + "type": "object", + "properties": { + "m_type": { + "$ref": "#/definitions/v3MType" + } + } + }, + "EndDeviceModelBattery": { + "type": "object", + "properties": { + "replaceable": { + "type": "boolean", + "description": "Whether the device battery can be replaced." + }, + "type": { + "type": "string", + "description": "Battery type." + } + } + }, + "EndDeviceModelCompliances": { + "type": "object", + "properties": { + "safety": { + "type": "array", + "items": { + "$ref": "#/definitions/CompliancesCompliance" + }, + "description": "List of safety standards the device is compliant with." + }, + "radio_equipment": { + "type": "array", + "items": { + "$ref": "#/definitions/CompliancesCompliance" + }, + "description": "List of radio equipment standards the device is compliant with." + } + } + }, + "EndDeviceModelDimensions": { + "type": "object", + "properties": { + "width": { + "type": "number", + "format": "float", + "description": "Device width (mm)." + }, + "height": { + "type": "number", + "format": "float", + "description": "Device height (mm)." + }, + "diameter": { + "type": "number", + "format": "float", + "description": "Device diameter (mm)." + }, + "length": { + "type": "number", + "format": "float", + "description": "Device length (mm)." + } + } + }, + "EndDeviceModelFirmwareVersion": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Firmware version string." + }, + "numeric": { + "type": "integer", + "format": "int64", + "description": "Numeric firmware revision number." + }, + "supported_hardware_versions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Hardware versions supported by this firmware version." + }, + "profiles": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FirmwareVersionProfile" + }, + "description": "Device profiles for each supported region (band)." + } + } + }, + "EndDeviceModelHardwareVersion": { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Hardware version string." + }, + "numeric": { + "type": "integer", + "format": "int64", + "description": "Numberic hardware revision number." + }, + "part_number": { + "type": "string", + "description": "Hardware part number." + } + } + }, + "EndDeviceModelOperatingConditions": { + "type": "object", + "properties": { + "temperature": { + "$ref": "#/definitions/OperatingConditionsLimits", + "description": "Temperature operating conditions (Celsius)." + }, + "relative_humidity": { + "$ref": "#/definitions/OperatingConditionsLimits", + "description": "Relative humidity operating conditions (Fraction, in range [0, 1])." + } + } + }, + "EndDeviceModelPhotos": { + "type": "object", + "properties": { + "main": { + "type": "string", + "description": "Main device photo." + }, + "other": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of other device photos." + } + } + }, + "EndDeviceModelReseller": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Reseller name." + }, + "region": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Reseller regions." + }, + "url": { + "type": "string", + "description": "Reseller URL." + } + } + }, + "EndDeviceModelVideos": { + "type": "object", + "properties": { + "main": { + "type": "string", + "description": "Link to main device video." + }, + "other": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Links to other device videos." + } + } + }, "EventAuthentication": { "type": "object", "properties": { "type": { - "type": "string" + "type": "string", + "description": "The type of authentication that was used. This is typically a bearer token." }, "token_type": { - "type": "string" + "type": "string", + "description": "The type of token that was used. Common types are APIKey, AccessToken and SessionToken." }, "token_id": { - "type": "string" + "type": "string", + "description": "The ID of the token that was used." + } + } + }, + "FirmwareVersionProfile": { + "type": "object", + "properties": { + "vendor_id": { + "type": "string", + "description": "Vendor ID of the profile, as defined in the Device Repository.\nIf this value is set, the profile is loaded from this vendor's folder.\nIf this value is not set, the profile is loaded from the current (end device's) vendor." }, - "remote_ip": { - "type": "string" + "profile_id": { + "type": "string", + "description": "Profile identifier, as defined in the Device Repository." + }, + "lorawan_certified": { + "type": "boolean", + "description": "Whether the device is LoRaWAN certified." + }, + "codec_id": { + "type": "string", + "description": "Payload formatter codec identifier, as defined in the Device Repository." } } }, @@ -7540,14 +17425,26 @@ }, "downlink_utilization_limit": { "type": "number", - "format": "float" + "format": "float", + "description": "Duty-cycle limit of the sub-band as a fraction of time." }, "downlink_utilization": { "type": "number", - "format": "float" + "format": "float", + "description": "Utilization rate of the available duty-cycle. This value should not exceed downlink_utilization_limit." } } }, + "GatewayLRFHSS": { + "type": "object", + "properties": { + "supported": { + "type": "boolean", + "description": "The gateway supports the LR-FHSS uplink channels." + } + }, + "description": "LR-FHSS gateway capabilities." + }, "GatewayRadioTxConfiguration": { "type": "object", "properties": { @@ -7563,14 +17460,66 @@ "type": "string", "format": "uint64" } - } + } + }, + "GenerateEndDeviceQRCodeRequestImage": { + "type": "object", + "properties": { + "image_size": { + "type": "integer", + "format": "int64", + "description": "Requested QR code image dimension in pixels." + } + } + }, + "GetClaimStatusResponseVendorSpecific": { + "type": "object", + "properties": { + "organization_unique_identifier": { + "type": "integer", + "format": "int64" + }, + "data": { + "type": "object", + "description": "Vendor Specific data in JSON format." + } + } + }, + "GetPhyVersionsResponseVersionInfo": { + "type": "object", + "properties": { + "band_id": { + "type": "string" + }, + "phy_versions": { + "type": "array", + "items": { + "$ref": "#/definitions/v3PHYVersion" + } + } + } + }, + "GetTemplateRequestEndDeviceProfileIdentifiers": { + "type": "object", + "properties": { + "vendor_id": { + "type": "integer", + "format": "int64", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005." + }, + "vendor_profile_id": { + "type": "integer", + "format": "int64", + "description": "ID of the LoRaWAN end device profile assigned by the vendor." + } + }, + "description": "Identifiers to uniquely identify a LoRaWAN end device profile." }, - "GenerateEndDeviceQRCodeRequestImage": { + "IsConfigurationAdminRights": { "type": "object", "properties": { - "image_size": { - "type": "integer", - "format": "int64" + "all": { + "type": "boolean" } } }, @@ -7578,8 +17527,7 @@ "type": "object", "properties": { "disable_upload": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -7587,12 +17535,18 @@ "type": "object", "properties": { "disable_upload": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "use_gravatar": { - "type": "boolean", - "format": "boolean" + "type": "boolean" + } + } + }, + "IsConfigurationUserLogin": { + "type": "object", + "properties": { + "disable_credentials_login": { + "type": "boolean" } } }, @@ -7610,6 +17564,9 @@ }, "password_requirements": { "$ref": "#/definitions/UserRegistrationPasswordRequirements" + }, + "enabled": { + "type": "boolean" } } }, @@ -7617,8 +17574,7 @@ "type": "object", "properties": { "required": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -7626,8 +17582,7 @@ "type": "object", "properties": { "required": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "token_ttl": { "type": "string" @@ -7638,20 +17593,27 @@ "type": "object", "properties": { "create_applications": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "create_clients": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "create_gateways": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "create_organizations": { - "type": "boolean", - "format": "boolean" + "type": "boolean" + } + } + }, + "ListBandsResponseVersionedBandDescription": { + "type": "object", + "properties": { + "band": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v3BandDescription" + } } } }, @@ -7672,8 +17634,7 @@ "type": "object", "properties": { "frequency_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -7703,12 +17664,10 @@ "type": "object", "properties": { "channel_index_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "frequency_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -7777,7 +17736,7 @@ "type": "object", "properties": { "rejoin_type": { - "$ref": "#/definitions/v3RejoinType" + "$ref": "#/definitions/v3RejoinRequestType" }, "data_rate_index": { "$ref": "#/definitions/v3DataRateIndex" @@ -7796,16 +17755,13 @@ "type": "object", "properties": { "channel_mask_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "data_rate_index_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "tx_power_index_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -7822,8 +17778,7 @@ "channel_mask": { "type": "array", "items": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } }, "channel_mask_control": { @@ -7854,473 +17809,914 @@ "type": "object", "properties": { "frequency_ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" + }, + "data_rate_ack": { + "type": "boolean" + } + } + }, + "MACCommandNewChannelReq": { + "type": "object", + "properties": { + "channel_index": { + "type": "integer", + "format": "int64" + }, + "frequency": { + "type": "string", + "format": "uint64" + }, + "min_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "max_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + } + } + }, + "MACCommandPingSlotChannelAns": { + "type": "object", + "properties": { + "frequency_ack": { + "type": "boolean" + }, + "data_rate_index_ack": { + "type": "boolean" + } + } + }, + "MACCommandPingSlotChannelReq": { + "type": "object", + "properties": { + "frequency": { + "type": "string", + "format": "uint64" + }, + "data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + } + } + }, + "MACCommandPingSlotInfoReq": { + "type": "object", + "properties": { + "period": { + "$ref": "#/definitions/v3PingSlotPeriod" + } + } + }, + "MACCommandRejoinParamSetupAns": { + "type": "object", + "properties": { + "max_time_exponent_ack": { + "type": "boolean" + } + } + }, + "MACCommandRejoinParamSetupReq": { + "type": "object", + "properties": { + "max_count_exponent": { + "$ref": "#/definitions/v3RejoinCountExponent", + "description": "Exponent e that configures the rejoin counter = 2^(e+4) messages." + }, + "max_time_exponent": { + "$ref": "#/definitions/v3RejoinTimeExponent", + "description": "Exponent e that configures the rejoin timer = 2^(e+10) seconds." + } + } + }, + "MACCommandRekeyConf": { + "type": "object", + "properties": { + "minor_version": { + "$ref": "#/definitions/v3Minor" + } + } + }, + "MACCommandRekeyInd": { + "type": "object", + "properties": { + "minor_version": { + "$ref": "#/definitions/v3Minor" + } + } + }, + "MACCommandResetConf": { + "type": "object", + "properties": { + "minor_version": { + "$ref": "#/definitions/v3Minor" + } + } + }, + "MACCommandResetInd": { + "type": "object", + "properties": { + "minor_version": { + "$ref": "#/definitions/v3Minor" + } + } + }, + "MACCommandRxParamSetupAns": { + "type": "object", + "properties": { + "rx2_data_rate_index_ack": { + "type": "boolean" + }, + "rx1_data_rate_offset_ack": { + "type": "boolean" + }, + "rx2_frequency_ack": { + "type": "boolean" + } + } + }, + "MACCommandRxParamSetupReq": { + "type": "object", + "properties": { + "rx2_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "rx1_data_rate_offset": { + "$ref": "#/definitions/v3DataRateOffset" + }, + "rx2_frequency": { + "type": "string", + "format": "uint64" + } + } + }, + "MACCommandRxTimingSetupReq": { + "type": "object", + "properties": { + "delay": { + "$ref": "#/definitions/v3RxDelay" + } + } + }, + "MACCommandTxParamSetupReq": { + "type": "object", + "properties": { + "max_eirp_index": { + "$ref": "#/definitions/v3DeviceEIRP", + "title": "Indicates the maximum EIRP value in dBm, indexed by the following vector:\n[ 8 10 12 13 14 16 18 20 21 24 26 27 29 30 33 36 ]" + }, + "uplink_dwell_time": { + "type": "boolean" + }, + "downlink_dwell_time": { + "type": "boolean" + } + } + }, + "MACStateDataRateRange": { + "type": "object", + "properties": { + "min_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "max_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + } + } + }, + "MACStateDataRateRanges": { + "type": "object", + "properties": { + "ranges": { + "type": "array", + "items": { + "$ref": "#/definitions/MACStateDataRateRange" + } + } + } + }, + "MACStateDownlinkMessageMessage": { + "type": "object", + "properties": { + "m_hdr": { + "$ref": "#/definitions/DownlinkMessageMessageMHDR" + }, + "mac_payload": { + "$ref": "#/definitions/DownlinkMessageMessageMACPayload" + } + } + }, + "MACStateJoinAccept": { + "type": "object", + "properties": { + "payload": { + "type": "string", + "format": "byte", + "description": "Payload of the join-accept received from Join Server." + }, + "request": { + "$ref": "#/definitions/v3MACStateJoinRequest" + }, + "keys": { + "$ref": "#/definitions/v3SessionKeys", + "description": "Network session keys associated with the join." + }, + "correlation_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD" + }, + "net_id": { + "type": "string", + "format": "string", + "example": "000013" + } + } + }, + "MACStateUplinkMessageRxMetadata": { + "type": "object", + "properties": { + "gateway_ids": { + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" + }, + "channel_rssi": { + "type": "number", + "format": "float" + }, + "snr": { + "type": "number", + "format": "float" + }, + "downlink_path_constraint": { + "$ref": "#/definitions/v3DownlinkPathConstraint" + }, + "uplink_token": { + "type": "string", + "format": "byte" }, - "data_rate_ack": { - "type": "boolean", - "format": "boolean" + "packet_broker": { + "$ref": "#/definitions/UplinkMessageRxMetadataPacketBrokerMetadata" } } }, - "MACCommandNewChannelReq": { + "MACStateUplinkMessageTxSettings": { "type": "object", "properties": { - "channel_index": { - "type": "integer", - "format": "int64" - }, - "frequency": { - "type": "string", - "format": "uint64" - }, - "min_data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex" - }, - "max_data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex" + "data_rate": { + "$ref": "#/definitions/v3DataRate" } } }, - "MACCommandPingSlotChannelAns": { + "MQTTProviderQoS": { + "type": "string", + "enum": [ + "AT_MOST_ONCE", + "AT_LEAST_ONCE", + "EXACTLY_ONCE" + ], + "default": "AT_MOST_ONCE" + }, + "OperatingConditionsLimits": { "type": "object", "properties": { - "frequency_ack": { - "type": "boolean", - "format": "boolean" + "min": { + "type": "number", + "format": "float", + "description": "Min value of operating conditions range." }, - "data_rate_index_ack": { - "type": "boolean", - "format": "boolean" + "max": { + "type": "number", + "format": "float", + "description": "Max value of operating conditions range." } } }, - "MACCommandPingSlotChannelReq": { + "PictureEmbedded": { "type": "object", "properties": { - "frequency": { + "mime_type": { "type": "string", - "format": "uint64" + "description": "MIME type of the picture." }, - "data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex" + "data": { + "type": "string", + "format": "byte", + "description": "Picture data. A data URI can be constructed as follows:\n`data:\u003cmime_type\u003e;base64,\u003cdata\u003e`." } } }, - "MACCommandPingSlotInfoReq": { + "ProvisionEndDevicesRequestIdentifiersFromData": { "type": "object", "properties": { - "period": { - "$ref": "#/definitions/v3PingSlotPeriod" + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" } } }, - "MACCommandRejoinParamSetupAns": { + "ProvisionEndDevicesRequestIdentifiersList": { "type": "object", "properties": { - "max_time_exponent_ack": { - "type": "boolean", - "format": "boolean" + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + }, + "end_device_ids": { + "type": "array", + "items": { + "$ref": "#/definitions/v3EndDeviceIdentifiers" + } } } }, - "MACCommandRejoinParamSetupReq": { + "ProvisionEndDevicesRequestIdentifiersRange": { "type": "object", "properties": { - "max_count_exponent": { - "$ref": "#/definitions/v3RejoinCountExponent", - "description": "Exponent e that configures the rejoin counter = 2^(e+4) messages." + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" }, - "max_time_exponent": { - "$ref": "#/definitions/v3RejoinTimeExponent", - "description": "Exponent e that configures the rejoin timer = 2^(e+10) seconds." + "start_dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "DevEUI to start issuing from." } } }, - "MACCommandRekeyConf": { + "PubSubProviders": { "type": "object", "properties": { - "minor_version": { - "$ref": "#/definitions/v3Minor" + "mqtt": { + "$ref": "#/definitions/PubSubProvidersStatus" + }, + "nats": { + "$ref": "#/definitions/PubSubProvidersStatus" } } }, - "MACCommandRekeyInd": { - "type": "object", - "properties": { - "minor_version": { - "$ref": "#/definitions/v3Minor" - } - } + "PubSubProvidersStatus": { + "type": "string", + "enum": [ + "ENABLED", + "WARNING", + "DISABLED" + ], + "default": "ENABLED", + "description": " - ENABLED: No restrictions are in place.\n - WARNING: Warnings are being emitted that the provider will be deprecated in the future.\n - DISABLED: New integrations cannot be set up, and old ones do not start." }, - "MACCommandResetConf": { + "TxAcknowledgmentResult": { + "type": "string", + "enum": [ + "SUCCESS", + "UNKNOWN_ERROR", + "TOO_LATE", + "TOO_EARLY", + "COLLISION_PACKET", + "COLLISION_BEACON", + "TX_FREQ", + "TX_POWER", + "GPS_UNLOCKED" + ], + "default": "SUCCESS" + }, + "TxSettingsDownlink": { "type": "object", "properties": { - "minor_version": { - "$ref": "#/definitions/v3Minor" + "antenna_index": { + "type": "integer", + "format": "int64", + "description": "Index of the antenna on which the uplink was received and/or downlink must be sent." + }, + "tx_power": { + "type": "number", + "format": "float", + "description": "Transmission power (dBm). Only on downlink." + }, + "invert_polarization": { + "type": "boolean", + "description": "Invert LoRa polarization; false for LoRaWAN uplink, true for downlink." } - } + }, + "description": "Transmission settings for downlink." }, - "MACCommandResetInd": { + "UplinkMessageRxMetadataPacketBrokerMetadata": { + "type": "object" + }, + "UserRegistrationAdminApproval": { "type": "object", "properties": { - "minor_version": { - "$ref": "#/definitions/v3Minor" + "required": { + "type": "boolean" } } }, - "MACCommandRxParamSetupAns": { + "UserRegistrationPasswordRequirements": { "type": "object", "properties": { - "rx2_data_rate_index_ack": { - "type": "boolean", - "format": "boolean" + "min_length": { + "type": "integer", + "format": "int64" }, - "rx1_data_rate_offset_ack": { - "type": "boolean", - "format": "boolean" + "max_length": { + "type": "integer", + "format": "int64" }, - "rx2_frequency_ack": { - "type": "boolean", - "format": "boolean" + "min_uppercase": { + "type": "integer", + "format": "int64" + }, + "min_digits": { + "type": "integer", + "format": "int64" + }, + "min_special": { + "type": "integer", + "format": "int64" } } }, - "MACCommandRxParamSetupReq": { + "googlerpcStatus": { "type": "object", "properties": { - "rx2_data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex" - }, - "rx1_data_rate_offset": { + "code": { "type": "integer", - "format": "int64" + "format": "int32" }, - "rx2_frequency": { - "type": "string", - "format": "uint64" + "message": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "$ref": "#/definitions/protobufAny" + } } } }, - "MACCommandRxTimingSetupReq": { + "lorawanv3BoolValue": { "type": "object", "properties": { - "delay": { - "$ref": "#/definitions/v3RxDelay" + "value": { + "type": "boolean" } } }, - "MACCommandTxParamSetupReq": { + "lorawanv3ContactInfoValidation": { "type": "object", "properties": { - "max_eirp_index": { - "$ref": "#/definitions/v3DeviceEIRP", - "title": "Indicates the maximum EIRP value in dBm, indexed by the following vector:\n[ 8 10 12 13 14 16 18 20 21 24 26 27 29 30 33 36 ]" + "id": { + "type": "string" }, - "uplink_dwell_time": { - "type": "boolean", - "format": "boolean" + "token": { + "type": "string" }, - "downlink_dwell_time": { - "type": "boolean", - "format": "boolean" + "entity": { + "$ref": "#/definitions/v3EntityIdentifiers" + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + } + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" } } }, - "MACStateJoinAccept": { + "lorawanv3DownlinkMessage": { "type": "object", "properties": { - "payload": { + "raw_payload": { "type": "string", - "format": "byte", - "description": "Payload of the join-accept received from Join Server." + "format": "byte" + }, + "payload": { + "$ref": "#/definitions/lorawanv3Message" + }, + "end_device_ids": { + "$ref": "#/definitions/v3EndDeviceIdentifiers" }, "request": { - "$ref": "#/definitions/v3JoinRequest", - "description": "JoinRequest sent to Join Server." + "$ref": "#/definitions/v3TxRequest" }, - "keys": { - "$ref": "#/definitions/v3SessionKeys", - "description": "Network session keys associated with the join." + "scheduled": { + "$ref": "#/definitions/lorawanv3TxSettings" }, "correlation_ids": { "type": "array", "items": { "type": "string" } + }, + "session_key_id": { + "type": "string", + "format": "byte" } - } + }, + "title": "Downlink message from the network to the end device" }, - "MQTTProviderQoS": { - "type": "string", - "enum": [ - "AT_MOST_ONCE", - "AT_LEAST_ONCE", - "EXACTLY_ONCE" - ], - "default": "AT_MOST_ONCE" + "lorawanv3GatewayIdentifiers": { + "type": "object", + "properties": { + "gateway_id": { + "type": "string" + }, + "eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Secondary identifier, which can only be used in specific requests." + } + } }, - "PictureEmbedded": { + "lorawanv3Invitation": { "type": "object", "properties": { - "mime_type": { + "email": { + "type": "string" + }, + "token": { + "type": "string" + }, + "expires_at": { "type": "string", - "description": "MIME type of the picture." + "format": "date-time" }, - "data": { + "created_at": { "type": "string", - "format": "byte", - "description": "Picture data. A data URI can be constructed as follows:\n`data:\u003cmime_type\u003e;base64,\u003cdata\u003e`." + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "accepted_at": { + "type": "string", + "format": "date-time" + }, + "accepted_by": { + "$ref": "#/definitions/v3UserIdentifiers" } } }, - "ProvisionEndDevicesRequestIdentifiersFromData": { + "lorawanv3Location": { "type": "object", "properties": { - "join_eui": { - "type": "string", - "format": "byte" + "latitude": { + "type": "number", + "format": "double", + "description": "The North–South position (degrees; -90 to +90), where 0 is the equator, North pole is positive, South pole is negative." + }, + "longitude": { + "type": "number", + "format": "double", + "description": "The East-West position (degrees; -180 to +180), where 0 is the Prime Meridian (Greenwich), East is positive , West is negative." + }, + "altitude": { + "type": "integer", + "format": "int32", + "description": "The altitude (meters), where 0 is the mean sea level." + }, + "accuracy": { + "type": "integer", + "format": "int32", + "description": "The accuracy of the location (meters)." + }, + "source": { + "$ref": "#/definitions/v3LocationSource", + "description": "Source of the location information." } } }, - "ProvisionEndDevicesRequestIdentifiersList": { + "lorawanv3MACPayload": { "type": "object", "properties": { - "join_eui": { + "f_hdr": { + "$ref": "#/definitions/v3FHDR" + }, + "f_port": { + "type": "integer", + "format": "int64" + }, + "frm_payload": { "type": "string", "format": "byte" }, - "end_device_ids": { - "type": "array", - "items": { - "$ref": "#/definitions/v3EndDeviceIdentifiers" - } + "decoded_payload": { + "type": "object" + }, + "full_f_cnt": { + "type": "integer", + "format": "int64", + "description": "Full 32-bit FCnt value. Used internally by Network Server." } } }, - "ProvisionEndDevicesRequestIdentifiersRange": { + "lorawanv3MHDR": { "type": "object", "properties": { - "join_eui": { - "type": "string", - "format": "byte" + "m_type": { + "$ref": "#/definitions/v3MType" }, - "start_dev_eui": { - "type": "string", - "format": "byte", - "description": "DevEUI to start issuing from." + "major": { + "$ref": "#/definitions/v3Major" } } }, - "TxAcknowledgmentResult": { - "type": "string", - "enum": [ - "SUCCESS", - "UNKNOWN_ERROR", - "TOO_LATE", - "TOO_EARLY", - "COLLISION_PACKET", - "COLLISION_BEACON", - "TX_FREQ", - "TX_POWER", - "GPS_UNLOCKED" - ], - "default": "SUCCESS" - }, - "TxSettingsDownlink": { + "lorawanv3Message": { "type": "object", "properties": { - "antenna_index": { - "type": "integer", - "format": "int64", - "description": "Index of the antenna on which the uplink was received and/or downlink must be sent." + "m_hdr": { + "$ref": "#/definitions/lorawanv3MHDR" }, - "tx_power": { - "type": "number", - "format": "float", - "description": "Transmission power (dBm). Only on downlink." + "mic": { + "type": "string", + "format": "byte" }, - "invert_polarization": { - "type": "boolean", - "format": "boolean", - "description": "Invert LoRa polarization; false for LoRaWAN uplink, true for downlink." + "mac_payload": { + "$ref": "#/definitions/lorawanv3MACPayload" + }, + "join_request_payload": { + "$ref": "#/definitions/v3JoinRequestPayload" + }, + "join_accept_payload": { + "$ref": "#/definitions/v3JoinAcceptPayload" + }, + "rejoin_request_payload": { + "$ref": "#/definitions/v3RejoinRequestPayload" } }, - "description": "Transmission settings for downlink." + "title": "Message represents a LoRaWAN message" }, - "UserRegistrationAdminApproval": { + "lorawanv3PacketBrokerMetadata": { "type": "object", "properties": { - "required": { - "type": "boolean", - "format": "boolean" + "message_id": { + "type": "string", + "description": "Message identifier generated by Packet Broker Router." + }, + "forwarder_net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "LoRa Alliance NetID of the Packet Broker Forwarder Member." + }, + "forwarder_tenant_id": { + "type": "string", + "description": "Tenant ID managed by the Packet Broker Forwarder Member." + }, + "forwarder_cluster_id": { + "type": "string", + "description": "Forwarder Cluster ID of the Packet Broker Forwarder." + }, + "forwarder_gateway_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "Forwarder gateway EUI." + }, + "forwarder_gateway_id": { + "type": "string", + "description": "Forwarder gateway ID." + }, + "home_network_net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "LoRa Alliance NetID of the Packet Broker Home Network Member." + }, + "home_network_tenant_id": { + "type": "string", + "description": "Tenant ID managed by the Packet Broker Home Network Member.\nThis value is empty if it cannot be determined by the Packet Broker Router." + }, + "home_network_cluster_id": { + "type": "string", + "description": "Home Network Cluster ID of the Packet Broker Home Network." + }, + "hops": { + "type": "array", + "items": { + "$ref": "#/definitions/v3PacketBrokerRouteHop" + }, + "description": "Hops that the message passed. Each Packet Broker Router service appends an entry." } } }, - "UserRegistrationPasswordRequirements": { + "lorawanv3RxMetadata": { "type": "object", "properties": { - "min_length": { - "type": "integer", - "format": "int64" + "gateway_ids": { + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" }, - "max_length": { - "type": "integer", - "format": "int64" + "packet_broker": { + "$ref": "#/definitions/lorawanv3PacketBrokerMetadata" }, - "min_uppercase": { + "antenna_index": { "type": "integer", "format": "int64" }, - "min_digits": { - "type": "integer", - "format": "int64" + "time": { + "type": "string", + "format": "date-time", + "description": "Timestamp at the end of the transmission, provided by the gateway. The accuracy is undefined." }, - "min_special": { + "timestamp": { "type": "integer", - "format": "int64" - } - } - }, - "lorawanv3ContactInfoValidation": { - "type": "object", - "properties": { - "id": { - "type": "string" + "format": "int64", + "description": "Gateway concentrator timestamp when the Rx finished (microseconds)." }, - "token": { + "fine_timestamp": { + "type": "string", + "format": "uint64", + "description": "Gateway's internal fine timestamp when the Rx finished (nanoseconds)." + }, + "encrypted_fine_timestamp": { + "type": "string", + "format": "byte", + "description": "Encrypted gateway's internal fine timestamp when the Rx finished (nanoseconds)." + }, + "encrypted_fine_timestamp_key_id": { "type": "string" }, - "entity": { - "$ref": "#/definitions/v3EntityIdentifiers" + "rssi": { + "type": "number", + "format": "float", + "description": "Received signal strength indicator (dBm).\nThis value equals `channel_rssi`." }, - "contact_info": { - "type": "array", - "items": { - "$ref": "#/definitions/v3ContactInfo" - } + "signal_rssi": { + "type": "number", + "format": "float", + "description": "Received signal strength indicator of the signal (dBm)." }, - "created_at": { + "channel_rssi": { + "type": "number", + "format": "float", + "description": "Received signal strength indicator of the channel (dBm)." + }, + "rssi_standard_deviation": { + "type": "number", + "format": "float", + "description": "Standard deviation of the RSSI during preamble." + }, + "snr": { + "type": "number", + "format": "float", + "description": "Signal-to-noise ratio (dB)." + }, + "frequency_offset": { "type": "string", - "format": "date-time" + "format": "int64", + "description": "Frequency offset (Hz)." + }, + "location": { + "$ref": "#/definitions/lorawanv3Location", + "description": "Antenna location; injected by the Gateway Server." + }, + "downlink_path_constraint": { + "$ref": "#/definitions/v3DownlinkPathConstraint", + "description": "Gateway downlink path constraint; injected by the Gateway Server." }, - "expires_at": { + "uplink_token": { "type": "string", - "format": "date-time" - } - } - }, - "lorawanv3Invitation": { - "type": "object", - "properties": { - "email": { - "type": "string" + "format": "byte", + "description": "Uplink token to be included in the Tx request in class A downlink; injected by gateway, Gateway Server or fNS." }, - "token": { - "type": "string" + "channel_index": { + "type": "integer", + "format": "int64", + "description": "Index of the gateway channel that received the message." }, - "expires_at": { - "type": "string", - "format": "date-time" + "hopping_width": { + "type": "integer", + "format": "int64", + "description": "Hopping width; a number describing the number of steps of the LR-FHSS grid." }, - "created_at": { - "type": "string", - "format": "date-time" + "frequency_drift": { + "type": "integer", + "format": "int32", + "description": "Frequency drift in Hz between start and end of an LR-FHSS packet (signed)." }, - "updated_at": { + "gps_time": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "Timestamp at the end of the transmission, provided by the gateway.\nGuaranteed to be based on a GPS PPS signal, with an accuracy of 1 millisecond." }, - "accepted_at": { + "received_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "Timestamp at which the Gateway Server has received the message." }, - "accepted_by": { - "$ref": "#/definitions/v3UserIdentifiers" + "advanced": { + "type": "object", + "title": "Advanced metadata fields\n- can be used for advanced information or experimental features that are not yet formally defined in the API\n- field names are written in snake_case" } - } + }, + "description": "Contains metadata for a received message. Each antenna that receives\na message corresponds to one RxMetadata." }, - "lorawanv3Location": { + "lorawanv3TxSettings": { "type": "object", "properties": { - "latitude": { - "type": "number", - "format": "double", - "description": "The North–South position (degrees; -90 to +90), where 0 is the equator, North pole is positive, South pole is negative." + "data_rate": { + "$ref": "#/definitions/v3DataRate", + "description": "Data rate." }, - "longitude": { - "type": "number", - "format": "double", - "description": "The East-West position (degrees; -180 to +180), where 0 is the Prime Meridian (Greenwich), East is positive , West is negative." + "frequency": { + "type": "string", + "format": "uint64", + "description": "Frequency (Hz)." }, - "altitude": { - "type": "integer", - "format": "int32", - "description": "The altitude (meters), where 0 is the mean sea level." + "enable_crc": { + "type": "boolean", + "description": "Send a CRC in the packet; only on uplink; on downlink, CRC should not be enabled." }, - "accuracy": { + "timestamp": { "type": "integer", - "format": "int32", - "description": "The accuracy of the location (meters)." + "format": "int64", + "description": "Timestamp of the gateway concentrator when the uplink message was received, or when the downlink message should be transmitted (microseconds).\nOn downlink, set timestamp to 0 and time to null to use immediate scheduling." }, - "source": { - "$ref": "#/definitions/v3LocationSource", - "description": "Source of the location information." + "time": { + "type": "string", + "format": "date-time", + "description": "Time of the gateway when the uplink message was received, or when the downlink message should be transmitted.\nFor downlink, this requires the gateway to have GPS time synchronization." + }, + "downlink": { + "$ref": "#/definitions/TxSettingsDownlink", + "description": "Transmission settings for downlink." + }, + "concentrator_timestamp": { + "type": "string", + "format": "int64", + "description": "Concentrator timestamp for the downlink as calculated by the Gateway Server scheduler.\nThis value takes into account necessary offsets such as the RTT (Round Trip Time) and TOA (Time Of Arrival).\nThis field is set and used only by the Gateway Server." } - } + }, + "description": "TxSettings contains the settings for a transmission.\nThis message is used on both uplink and downlink.\nOn downlink, this is a scheduled transmission." }, - "lorawanv3Message": { + "lorawanv3UplinkMessage": { "type": "object", "properties": { - "m_hdr": { - "$ref": "#/definitions/v3MHDR" - }, - "mic": { + "raw_payload": { "type": "string", "format": "byte" }, - "mac_payload": { - "$ref": "#/definitions/v3MACPayload" + "payload": { + "$ref": "#/definitions/lorawanv3Message" }, - "join_request_payload": { - "$ref": "#/definitions/v3JoinRequestPayload" + "settings": { + "$ref": "#/definitions/lorawanv3TxSettings" }, - "join_accept_payload": { - "$ref": "#/definitions/v3JoinAcceptPayload" + "rx_metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/lorawanv3RxMetadata" + } }, - "rejoin_request_payload": { - "$ref": "#/definitions/v3RejoinRequestPayload" - } - } - }, - "protobufAny": { - "type": "object", - "properties": { - "type_url": { + "received_at": { "type": "string", - "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." + "format": "date-time", + "description": "Server time when a component received the message.\nThe Gateway Server and Network Server set this value to their local server time of reception." }, - "value": { + "correlation_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "device_channel_index": { + "type": "integer", + "format": "int64", + "description": "Index of the device channel that received the message.\nSet by Network Server." + }, + "consumed_airtime": { "type": "string", - "format": "byte", - "description": "Must be a valid serialized protocol buffer of the above specified type." + "description": "Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the RawPayload size and the transmission settings." } }, - "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := \u0026pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" + "title": "Uplink message from the end device to the network" }, - "protobufFieldMask": { + "protobufAny": { "type": "object", "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The set of field mask paths." + "@type": { + "type": "string", + "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n URL, or have them precompiled into a binary to avoid any\n lookup. Therefore, binary compatibility needs to be preserved\n on changes to types. (Use versioned type names to manage\n breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics." } }, - "description": "paths: \"f.a\"\n paths: \"f.b.d\"\n\nHere `f` represents a field in some root message, `a` and `b`\nfields in the message found in `f`, and `d` a field found in the\nmessage in `f.b`.\n\nField masks are used to specify a subset of fields that should be\nreturned by a get operation or modified by an update operation.\nField masks also have a custom JSON encoding (see below).\n\n# Field Masks in Projections\n\nWhen used in the context of a projection, a response message or\nsub-message is filtered by the API to only contain those fields as\nspecified in the mask. For example, if the mask in the previous\nexample is applied to a response message as follows:\n\n f {\n a : 22\n b {\n d : 1\n x : 2\n }\n y : 13\n }\n z: 8\n\nThe result will not contain specific values for fields x,y and z\n(their value will be set to the default, and omitted in proto text\noutput):\n\n\n f {\n a : 22\n b {\n d : 1\n }\n }\n\nA repeated field is not allowed except at the last position of a\npaths string.\n\nIf a FieldMask object is not present in a get operation, the\noperation applies to all fields (as if a FieldMask of all fields\nhad been specified).\n\nNote that a field mask does not necessarily apply to the\ntop-level response message. In case of a REST get operation, the\nfield mask applies directly to the response, but in case of a REST\nlist operation, the mask instead applies to each individual message\nin the returned resource list. In case of a REST custom method,\nother definitions may be used. Where the mask applies will be\nclearly documented together with its declaration in the API. In\nany case, the effect on the returned resource/resources is required\nbehavior for APIs.\n\n# Field Masks in Update Operations\n\nA field mask in update operations specifies which fields of the\ntargeted resource are going to be updated. The API is required\nto only change the values of the fields as specified in the mask\nand leave the others untouched. If a resource is passed in to\ndescribe the updated values, the API ignores the values of all\nfields not covered by the mask.\n\nIf a repeated field is specified for an update operation, new values will\nbe appended to the existing repeated field in the target resource. Note that\na repeated field is only allowed in the last position of a `paths` string.\n\nIf a sub-message is specified in the last position of the field mask for an\nupdate operation, then new value will be merged into the existing sub-message\nin the target resource.\n\nFor example, given the target message:\n\n f {\n b {\n d: 1\n x: 2\n }\n c: [1]\n }\n\nAnd an update message:\n\n f {\n b {\n d: 10\n }\n c: [2]\n }\n\nthen if the field mask is:\n\n paths: [\"f.b\", \"f.c\"]\n\nthen the result will be:\n\n f {\n b {\n d: 10\n x: 2\n }\n c: [1, 2]\n }\n\nAn implementation may provide options to override this default behavior for\nrepeated and message fields.\n\nIn order to reset a field's value to the default, the field must\nbe in the mask and set to the default value in the provided resource.\nHence, in order to reset all fields of a resource, provide a default\ninstance of the resource and set all fields in the mask, or do\nnot provide a mask as described below.\n\nIf a field mask is not present on update, the operation applies to\nall fields (as if a field mask of all fields has been specified).\nNote that in the presence of schema evolution, this may mean that\nfields the client does not know and has therefore not filled into\nthe request will be reset to their default. If this is unwanted\nbehavior, a specific service may require a client to always specify\na field mask, producing an error if not.\n\nAs with get operations, the location of the resource which\ndescribes the updated values in the request message depends on the\noperation kind. In any case, the effect of the field mask is\nrequired to be honored by the API.\n\n## Considerations for HTTP REST\n\nThe HTTP kind of an update operation which uses a field mask must\nbe set to PATCH instead of PUT in order to satisfy HTTP semantics\n(PUT must only be used for full updates).\n\n# JSON Encoding of Field Masks\n\nIn JSON, a field mask is encoded as a single string where paths are\nseparated by a comma. Fields name in each path are converted\nto/from lower-camel naming conventions.\n\nAs an example, consider the following message declarations:\n\n message Profile {\n User user = 1;\n Photo photo = 2;\n }\n message User {\n string display_name = 1;\n string address = 2;\n }\n\nIn proto a field mask for `Profile` may look as such:\n\n mask {\n paths: \"user.display_name\"\n paths: \"photo\"\n }\n\nIn JSON, the same mask is represented as below:\n\n {\n mask: \"user.displayName,photo\"\n }\n\n# Field Masks and Oneof Fields\n\nField masks treat fields in oneofs just as regular fields. Consider the\nfollowing message:\n\n message SampleMessage {\n oneof test_oneof {\n string name = 4;\n SubMessage sub_message = 9;\n }\n }\n\nThe field mask can be:\n\n mask {\n paths: \"name\"\n }\n\nOr:\n\n mask {\n paths: \"sub_message\"\n }\n\nNote that oneof type names (\"test_oneof\" in this case) cannot be used in\npaths.\n\n## Field Mask Verification\n\nThe implementation of any API method which has a FieldMask type field in the\nrequest should verify the included field paths, and return an\n`INVALID_ARGUMENT` error if any path is unmappable.", - "title": "`FieldMask` represents a set of symbolic field paths, for example:" + "additionalProperties": {}, + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\nExample 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nExample 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "protobufNullValue": { "type": "string", @@ -8330,52 +18726,6 @@ "default": "NULL_VALUE", "description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value." }, - "runtimeError": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/protobufAny" - } - } - } - }, - "runtimeStreamError": { - "type": "object", - "properties": { - "grpc_code": { - "type": "integer", - "format": "int32" - }, - "http_code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "http_status": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/protobufAny" - } - } - } - }, "v3ADRAckDelayExponent": { "type": "string", "enum": [ @@ -8436,6 +18786,21 @@ } } }, + "v3ADRSettings": { + "type": "object", + "properties": { + "static": { + "$ref": "#/definitions/ADRSettingsStaticMode" + }, + "dynamic": { + "$ref": "#/definitions/ADRSettingsDynamicMode" + }, + "disabled": { + "$ref": "#/definitions/ADRSettingsDisabledMode" + } + }, + "description": "Adaptive Data Rate settings." + }, "v3APIKey": { "type": "object", "properties": { @@ -8457,6 +18822,18 @@ "$ref": "#/definitions/v3Right" }, "description": "Rights that are granted to this API key." + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" } } }, @@ -8514,21 +18891,31 @@ "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" + "$ref": "#/definitions/v3ApplicationIdentifiers", + "description": "The identifiers of the application. These are public and can be seen by any authenticated user in the network." }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the application was created. This information is public and can be seen by any authenticated user in the network." }, "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the application was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the application was deleted. This information is public and can be seen by any authenticated user in the network." }, "name": { - "type": "string" + "type": "string", + "description": "The name of the application." }, "description": { - "type": "string" + "type": "string", + "description": "A description for the application." }, "attributes": { "type": "object", @@ -8542,11 +18929,56 @@ "items": { "$ref": "#/definitions/v3ContactInfo" }, - "description": "Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application." + "description": "Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "network_server_address": { + "type": "string", + "description": "The address of the Network Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "application_server_address": { + "type": "string", + "description": "The address of the Application Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "join_server_address": { + "type": "string", + "description": "The address of the Join Server where this application is supposed to be registered.\nIf set, this fields indicates where end devices for this application should be registered.\n\nStored in Entity Registry.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + }, + "dev_eui_counter": { + "type": "integer", + "format": "int64" } }, "description": "Application is the message that defines an Application in the network." }, + "v3ApplicationActivationSettings": { + "type": "object", + "properties": { + "kek_label": { + "type": "string", + "description": "The KEK label to use for wrapping application keys." + }, + "kek": { + "$ref": "#/definitions/v3KeyEnvelope", + "description": "The (encrypted) Key Encryption Key." + }, + "home_net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "Home NetID." + }, + "application_server_id": { + "type": "string", + "description": "The AS-ID of the Application Server to use." + } + } + }, "v3ApplicationDownlink": { "type": "object", "properties": { @@ -8569,11 +19001,18 @@ "description": "The frame payload of the downlink message.\nThe payload is encrypted if the skip_payload_crypto field of the EndDevice\nis true." }, "decoded_payload": { - "type": "object" + "type": "object", + "description": "The decoded frame payload of the downlink message.\nWhen scheduling downlink with a message processor configured for the end device (see formatters) or application (see default_formatters),\nthis fields acts as input for the downlink encoder, and the output is set to frm_payload.\nWhen reading downlink (listing the queue, downlink message events, etc), this fields acts as output of the downlink decoder, and the input is frm_payload." + }, + "decoded_payload_warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Warnings generated by the message processor while encoding frm_payload (scheduling downlink) or decoding the frm_payload (reading downlink)." }, "confirmed": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "class_b_c": { "$ref": "#/definitions/ApplicationDownlinkClassBC", @@ -8633,6 +19072,10 @@ "last_f_cnt_down": { "type": "integer", "format": "int64" + }, + "session_key_id": { + "type": "string", + "format": "byte" } } }, @@ -8657,7 +19100,6 @@ }, "pending_session": { "type": "boolean", - "format": "boolean", "description": "Indicates whether the security context refers to the pending session, i.e. when this join-accept is an answer to a\nrejoin-request." }, "received_at": { @@ -8670,24 +19112,12 @@ "v3ApplicationLink": { "type": "object", "properties": { - "network_server_address": { - "type": "string", - "description": "The address of the external Network Server where to link to.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nLeave empty when linking to a cluster Network Server." - }, - "api_key": { - "type": "string" - }, "default_formatters": { - "$ref": "#/definitions/v3MessagePayloadFormatters" - }, - "tls": { - "type": "boolean", - "format": "boolean", - "description": "Enable TLS for linking to the external Network Server.\nFor cluster-local Network Servers, the cluster's TLS setting is used." + "$ref": "#/definitions/v3MessagePayloadFormatters", + "description": "Default message payload formatters to use when there are no formatters\ndefined on the end device level." }, "skip_payload_crypto": { "type": "boolean", - "format": "boolean", "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nLeave empty for the using the Application Server's default setting." } } @@ -8879,6 +19309,9 @@ "mqtt": { "$ref": "#/definitions/ApplicationPubSubMQTTProvider" }, + "aws_iot": { + "$ref": "#/definitions/ApplicationPubSubAWSIoTProvider" + }, "base_topic": { "type": "string", "description": "Base topic name to which the messages topic is appended." @@ -8894,6 +19327,9 @@ "uplink_message": { "$ref": "#/definitions/v3ApplicationPubSubMessage" }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, "join_accept": { "$ref": "#/definitions/v3ApplicationPubSubMessage" }, @@ -8912,6 +19348,9 @@ "downlink_queued": { "$ref": "#/definitions/v3ApplicationPubSubMessage" }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationPubSubMessage" + }, "location_solved": { "$ref": "#/definitions/v3ApplicationPubSubMessage" }, @@ -8994,6 +19433,9 @@ "uplink_message": { "$ref": "#/definitions/v3ApplicationUplink" }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationUplinkNormalized" + }, "join_accept": { "$ref": "#/definitions/v3ApplicationJoinAccept" }, @@ -9023,10 +19465,10 @@ }, "simulated": { "type": "boolean", - "format": "boolean", "description": "Signals if the message is coming from the Network Server or is simulated." } - } + }, + "description": "Application uplink message." }, "v3ApplicationUplink": { "type": "object", @@ -9038,11 +19480,13 @@ }, "f_port": { "type": "integer", - "format": "int64" + "format": "int64", + "description": "LoRaWAN FPort of the uplink message." }, "f_cnt": { "type": "integer", - "format": "int64" + "format": "int64", + "description": "LoRaWAN FCntUp of the uplink message." }, "frm_payload": { "type": "string", @@ -9050,18 +19494,40 @@ "description": "The frame payload of the uplink message.\nThe payload is still encrypted if the skip_payload_crypto field of the EndDevice\nis true, which is indicated by the presence of the app_s_key field." }, "decoded_payload": { - "type": "object" + "type": "object", + "description": "The decoded frame payload of the uplink message.\nThis field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters)." + }, + "decoded_payload_warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Warnings generated by the message processor while decoding the frm_payload." + }, + "normalized_payload": { + "type": "array", + "items": { + "type": "object" + }, + "description": "The normalized frame payload of the uplink message.\nThis field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters).\nIf the message processor is a custom script, there is no uplink normalizer script and the decoded output is valid\nnormalized payload, this field contains the decoded payload." + }, + "normalized_payload_warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Warnings generated by the message processor while normalizing the decoded payload." }, "rx_metadata": { "type": "array", "items": { - "$ref": "#/definitions/v3RxMetadata" + "$ref": "#/definitions/lorawanv3RxMetadata" }, "description": "A list of metadata for each antenna of each gateway that received this message." }, "settings": { - "$ref": "#/definitions/v3TxSettings", - "description": "Settings for the transmission." + "$ref": "#/definitions/lorawanv3TxSettings", + "description": "Transmission settings used by the end device." }, "received_at": { "type": "string", @@ -9079,7 +19545,101 @@ }, "confirmed": { "type": "boolean", - "format": "boolean" + "description": "Indicates whether the end device used confirmed data uplink." + }, + "consumed_airtime": { + "type": "string", + "description": "Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." + } + } + }, + "v3ApplicationUplinkNormalized": { + "type": "object", + "properties": { + "session_key_id": { + "type": "string", + "format": "byte", + "description": "Join Server issued identifier for the session keys used by this uplink." + }, + "f_port": { + "type": "integer", + "format": "int64", + "description": "LoRaWAN FPort of the uplink message." + }, + "f_cnt": { + "type": "integer", + "format": "int64", + "description": "LoRaWAN FCntUp of the uplink message." + }, + "frm_payload": { + "type": "string", + "format": "byte", + "description": "The frame payload of the uplink message.\nThis field is always decrypted with AppSKey." + }, + "normalized_payload": { + "type": "object", + "description": "The normalized frame payload of the uplink message.\nThis field is set for each item in normalized_payload in the corresponding ApplicationUplink message." + }, + "normalized_payload_warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "This field is set to normalized_payload_warnings in the corresponding ApplicationUplink message." + }, + "rx_metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/lorawanv3RxMetadata" + }, + "description": "A list of metadata for each antenna of each gateway that received this message." + }, + "settings": { + "$ref": "#/definitions/lorawanv3TxSettings", + "description": "Transmission settings used by the end device." + }, + "received_at": { + "type": "string", + "format": "date-time", + "description": "Server time when the Network Server received the message." + }, + "confirmed": { + "type": "boolean", + "description": "Indicates whether the end device used confirmed data uplink." + }, + "consumed_airtime": { + "type": "string", + "description": "Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings." + }, + "locations": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/lorawanv3Location" + }, + "description": "End device location metadata, set by the Application Server while handling the message." + }, + "version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device version identifiers, set by the Application Server while handling the message." + }, + "network_ids": { + "$ref": "#/definitions/v3NetworkIdentifiers", + "description": "Network identifiers, set by the Network Server that handles the message." } } }, @@ -9130,6 +19690,9 @@ "uplink_message": { "$ref": "#/definitions/v3ApplicationWebhookMessage" }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, "join_accept": { "$ref": "#/definitions/v3ApplicationWebhookMessage" }, @@ -9148,11 +19711,20 @@ "downlink_queued": { "$ref": "#/definitions/v3ApplicationWebhookMessage" }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, "location_solved": { "$ref": "#/definitions/v3ApplicationWebhookMessage" }, "service_data": { "$ref": "#/definitions/v3ApplicationWebhookMessage" + }, + "health_status": { + "$ref": "#/definitions/v3ApplicationWebhookHealth" + }, + "field_mask": { + "type": "string" } } }, @@ -9168,6 +19740,17 @@ } } }, + "v3ApplicationWebhookHealth": { + "type": "object", + "properties": { + "healthy": { + "$ref": "#/definitions/ApplicationWebhookHealthWebhookHealthStatusHealthy" + }, + "unhealthy": { + "$ref": "#/definitions/ApplicationWebhookHealthWebhookHealthStatusUnhealthy" + } + } + }, "v3ApplicationWebhookIdentifiers": { "type": "object", "properties": { @@ -9231,12 +19814,14 @@ }, "create_downlink_api_key": { "type": "boolean", - "format": "boolean", "description": "Control the creation of the downlink queue operations API key." }, "uplink_message": { "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" }, + "uplink_normalized": { + "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" + }, "join_accept": { "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" }, @@ -9255,11 +19840,17 @@ "downlink_queued": { "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" }, + "downlink_queue_invalidated": { + "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" + }, "location_solved": { "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" }, "service_data": { "$ref": "#/definitions/v3ApplicationWebhookTemplateMessage" + }, + "field_mask": { + "type": "string" } } }, @@ -9277,11 +19868,13 @@ }, "secret": { "type": "boolean", - "format": "boolean", "description": "Secret decides if the field should be shown in plain-text or should stay hidden." }, "default_value": { "type": "string" + }, + "optional": { + "type": "boolean" } }, "description": "ApplicationWebhookTemplateField represents a custom field that needs to be filled by the user in order to use the template.\nA field can be an API key, an username or password, or any custom platform specific field (such as region).\nThe fields are meant to be replaced inside the URLs and headers when the webhook is created." @@ -9336,6 +19929,18 @@ } } }, + "v3AsConfiguration": { + "type": "object", + "properties": { + "pubsub": { + "$ref": "#/definitions/AsConfigurationPubSub" + }, + "webhooks": { + "$ref": "#/definitions/AsConfigurationWebhooks" + } + }, + "description": "Application Server configuration." + }, "v3AuthInfoResponse": { "type": "object", "properties": { @@ -9349,23 +19954,167 @@ "$ref": "#/definitions/v3UserSession", "description": "Warning: A user authorized by session cookie will be granted all\ncurrent and future rights. When using this auth type, the respective\nhandlers need to ensure thorough CSRF and CORS protection using\nappropriate middleware." }, + "gateway_token": { + "$ref": "#/definitions/AuthInfoResponseGatewayToken" + }, "universal_rights": { "$ref": "#/definitions/v3Rights" }, "is_admin": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, - "v3AuthorizeApplicationRequest": { + "v3BandDescription": { "type": "object", "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" + "id": { + "type": "string" }, - "api_key": { + "beacon": { + "$ref": "#/definitions/BandDescriptionBeacon" + }, + "ping_slot_frequencies": { + "type": "array", + "items": { + "type": "string", + "format": "uint64" + } + }, + "max_uplink_channels": { + "type": "integer", + "format": "int64" + }, + "uplink_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/v3BandDescriptionChannel" + } + }, + "max_downlink_channels": { + "type": "integer", + "format": "int64" + }, + "downlink_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/v3BandDescriptionChannel" + } + }, + "sub_bands": { + "type": "array", + "items": { + "$ref": "#/definitions/BandDescriptionSubBandParameters" + } + }, + "data_rates": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/BandDescriptionBandDataRate" + } + }, + "freq_multiplier": { + "type": "string", + "format": "uint64" + }, + "implements_cf_list": { + "type": "boolean" + }, + "cf_list_type": { + "$ref": "#/definitions/v3CFListType" + }, + "receive_delay_1": { + "type": "string" + }, + "receive_delay_2": { + "type": "string" + }, + "join_accept_delay_1": { + "type": "string" + }, + "join_accept_delay_2": { + "type": "string" + }, + "max_fcnt_gap": { + "type": "string", + "format": "uint64" + }, + "supports_dynamic_adr": { + "type": "boolean" + }, + "adr_ack_limit": { + "$ref": "#/definitions/v3ADRAckLimitExponent" + }, + "min_retransmit_timeout": { "type": "string" + }, + "max_retransmit_timeout": { + "type": "string" + }, + "tx_offset": { + "type": "array", + "items": { + "type": "number", + "format": "float" + } + }, + "max_adr_data_rate_index": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "tx_param_setup_req_support": { + "type": "boolean" + }, + "default_max_eirp": { + "type": "number", + "format": "float" + }, + "default_rx2_parameters": { + "$ref": "#/definitions/BandDescriptionRx2Parameters" + }, + "boot_dwell_time": { + "$ref": "#/definitions/BandDescriptionDwellTime" + } + } + }, + "v3BandDescriptionChannel": { + "type": "object", + "properties": { + "frequency": { + "type": "string", + "format": "uint64" + }, + "min_data_rate": { + "$ref": "#/definitions/v3DataRateIndex" + }, + "max_data_rate": { + "$ref": "#/definitions/v3DataRateIndex" + } + } + }, + "v3BatchGetGatewayConnectionStatsRequest": { + "type": "object", + "properties": { + "gateway_ids": { + "type": "array", + "items": { + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" + } + }, + "field_mask": { + "type": "string", + "description": "The names of the gateway stats fields that should be returned.\nThis mask will be applied on each entry returned." + } + } + }, + "v3BatchGetGatewayConnectionStatsResponse": { + "type": "object", + "properties": { + "entries": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v3GatewayConnectionStats" + }, + "description": "The map key is the gateway identifier." } } }, @@ -9386,8 +20135,7 @@ "ch_masks": { "type": "array", "items": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "description": "ChMasks controlling the channels to be used.\nLength of this field must be equal to the amount of uplink channels\ndefined by the selected frequency plan." } @@ -9401,15 +20149,43 @@ ], "default": "FREQUENCIES" }, + "v3CUPSRedirection": { + "type": "object", + "properties": { + "target_cups_uri": { + "type": "string", + "description": "CUPS URI for LoRa Basics Station CUPS redirection." + }, + "current_gateway_key": { + "type": "string", + "description": "The key set in the gateway to authenticate itself." + }, + "target_cups_trust": { + "type": "string", + "format": "byte", + "description": "Optional PEM encoded CA Root certificate. If this field is empty, DCS will attempt to dial the Target CUPS server and fetch the CA." + }, + "client_tls": { + "$ref": "#/definitions/CUPSRedirectionClientTLS", + "title": "TODO: Support mTLS (https://github.com/TheThingsNetwork/lorawan-stack/issues/137)" + }, + "auth_token": { + "type": "string", + "description": "The Device Claiming Server will fill this field with a The Things Stack API Key." + } + } + }, "v3ClaimEndDeviceRequest": { "type": "object", "properties": { "authenticated_identifiers": { - "$ref": "#/definitions/ClaimEndDeviceRequestAuthenticatedIdentifiers" + "$ref": "#/definitions/v3ClaimEndDeviceRequestAuthenticatedIdentifiers", + "description": "Authenticated identifiers." }, "qr_code": { "type": "string", - "format": "byte" + "format": "byte", + "description": "Raw QR code contents." }, "target_application_ids": { "$ref": "#/definitions/v3ApplicationIdentifiers", @@ -9441,16 +20217,83 @@ }, "target_net_id": { "type": "string", - "format": "byte", + "format": "string", + "example": "000013", "description": "Home NetID." }, "invalidate_authentication_code": { "type": "boolean", - "format": "boolean", "description": "If set, invalidate the authentication code with which the device gets claimed. This prohibits subsequent claiming requests." } } }, + "v3ClaimEndDeviceRequestAuthenticatedIdentifiers": { + "type": "object", + "properties": { + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "JoinEUI (or AppEUI) of the device to claim." + }, + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD", + "description": "DevEUI of the device to claim." + }, + "authentication_code": { + "type": "string", + "description": "Authentication code to prove ownership.\nIn the LoRa Alliance TR005 specification, this equals the OwnerToken." + } + } + }, + "v3ClaimGatewayRequest": { + "type": "object", + "properties": { + "authenticated_identifiers": { + "$ref": "#/definitions/v3ClaimGatewayRequestAuthenticatedIdentifiers" + }, + "qr_code": { + "type": "string", + "format": "byte" + }, + "collaborator": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers", + "description": "Collaborator to grant all rights on the target gateway." + }, + "target_gateway_id": { + "type": "string", + "description": "Gateway ID for the target gateway. This must be a unique value.\nIf this is not set, the target ID for the target gateway will be set to `\u003cgateway-eui\u003e`." + }, + "target_gateway_server_address": { + "type": "string", + "description": "Target Gateway Server Address for the target gateway." + }, + "cups_redirection": { + "$ref": "#/definitions/v3CUPSRedirection", + "description": "Parameters to set CUPS redirection for the gateway." + }, + "target_frequency_plan_id": { + "type": "string", + "description": "Frequency plan ID of the target gateway.\nThis equals the first element of the frequency_plan_ids field." + } + } + }, + "v3ClaimGatewayRequestAuthenticatedIdentifiers": { + "type": "object", + "properties": { + "gateway_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + }, + "authentication_code": { + "type": "string", + "format": "byte" + } + } + }, "v3Class": { "type": "string", "enum": [ @@ -9460,25 +20303,51 @@ ], "default": "CLASS_A" }, + "v3ClassBCGatewayIdentifiers": { + "type": "object", + "properties": { + "gateway_ids": { + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" + }, + "antenna_index": { + "type": "integer", + "format": "int64" + }, + "group_index": { + "type": "integer", + "format": "int64" + } + } + }, "v3Client": { "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3ClientIdentifiers" + "$ref": "#/definitions/v3ClientIdentifiers", + "description": "The identifiers of the OAuth client. These are public and can be seen by any authenticated user in the network." }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the OAuth client was created. This information is public and can be seen by any authenticated user in the network." }, "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the OAuth client was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the OAuth client was deleted. This information is public and can be seen by any authenticated user in the network." }, "name": { - "type": "string" + "type": "string", + "description": "The name of the OAuth client. This information is public and can be seen by any authenticated user in the network." }, "description": { - "type": "string" + "type": "string", + "description": "A description for the OAuth client. This information is public and can be seen by any authenticated user in the network." }, "attributes": { "type": "object", @@ -9492,7 +20361,13 @@ "items": { "$ref": "#/definitions/v3ContactInfo" }, - "description": "Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application." + "description": "Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application.\nThis information is public and can be seen by any authenticated user in the network.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" }, "secret": { "type": "string", @@ -9503,42 +20378,44 @@ "items": { "type": "string" }, - "description": "The allowed redirect URIs against which authorization requests are checked.\nIf the authorization request does not pass a redirect URI, the first one\nfrom this list is taken." + "description": "The allowed redirect URIs against which authorization requests are checked.\nIf the authorization request does not pass a redirect URI, the first one\nfrom this list is taken.\nThis information is public and can be seen by any authenticated user in the network." }, "logout_redirect_uris": { "type": "array", "items": { "type": "string" }, - "description": "The allowed logout redirect URIs against which client initiated logout\nrequests are checked. If the authorization request does not pass a redirect\nURI, the first one from this list is taken." + "description": "The allowed logout redirect URIs against which client initiated logout\nrequests are checked. If the authorization request does not pass a redirect\nURI, the first one from this list is taken.\nThis information is public and can be seen by any authenticated user in the network." }, "state": { "$ref": "#/definitions/v3State", - "description": "The reviewing state of the client.\nThis field can only be modified by admins." + "description": "The reviewing state of the client.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins.\nIf state_description is not updated when updating state, state_description is cleared." + }, + "state_description": { + "type": "string", + "description": "A description for the state field.\nThis field can only be modified by admins, and should typically only be updated\nwhen also updating `state`." }, "skip_authorization": { "type": "boolean", - "format": "boolean", - "description": "If set, the authorization page will be skipped.\nThis field can only be modified by admins." + "description": "If set, the authorization page will be skipped.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." }, "endorsed": { "type": "boolean", - "format": "boolean", - "description": "If set, the authorization page will show endorsement.\nThis field can only be modified by admins." + "description": "If set, the authorization page will show endorsement.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." }, "grants": { "type": "array", "items": { "$ref": "#/definitions/v3GrantType" }, - "description": "OAuth flows that can be used for the client to get a token.\nAfter a client is created, this field can only be modified by admins." + "description": "OAuth flows that can be used for the client to get a token.\nThis information is public and can be seen by any authenticated user in the network.\nAfter a client is created, this field can only be modified by admins." }, "rights": { "type": "array", "items": { "$ref": "#/definitions/v3Right" }, - "description": "Rights denotes what rights the client will have access to.\nUsers that previously authorized this client will have to re-authorize the\nclient after rights are added to this list." + "description": "Rights denotes what rights the client will have access to.\nThis information is public and can be seen by any authenticated user in the network.\nUsers that previously authorized this client will have to re-authorize the\nclient after rights are added to this list." } }, "description": "An OAuth client on the network." @@ -9647,8 +20524,7 @@ "type": "string" }, "public": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "validated_at": { "type": "string", @@ -9686,130 +20562,27 @@ "type": "string", "format": "byte", "description": "Data to convert." - } - } - }, - "v3CreateApplicationAPIKeyRequest": { - "type": "object", - "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" - }, - "name": { - "type": "string" - }, - "rights": { - "type": "array", - "items": { - "$ref": "#/definitions/v3Right" - } - } - } - }, - "v3CreateApplicationRequest": { - "type": "object", - "properties": { - "application": { - "$ref": "#/definitions/v3Application" - }, - "collaborator": { - "$ref": "#/definitions/v3OrganizationOrUserIdentifiers", - "description": "Collaborator to grant all rights on the newly created application." - } - } - }, - "v3CreateClientRequest": { - "type": "object", - "properties": { - "client": { - "$ref": "#/definitions/v3Client" - }, - "collaborator": { - "$ref": "#/definitions/v3OrganizationOrUserIdentifiers", - "description": "Collaborator to grant all rights on the newly created client." - } - } - }, - "v3CreateEndDeviceRequest": { - "type": "object", - "properties": { - "end_device": { - "$ref": "#/definitions/v3EndDevice" - } - } - }, - "v3CreateGatewayAPIKeyRequest": { - "type": "object", - "properties": { - "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" - }, - "name": { - "type": "string" - }, - "rights": { - "type": "array", - "items": { - "$ref": "#/definitions/v3Right" - } - } - } - }, - "v3CreateGatewayRequest": { - "type": "object", - "properties": { - "gateway": { - "$ref": "#/definitions/v3Gateway" - }, - "collaborator": { - "$ref": "#/definitions/v3OrganizationOrUserIdentifiers", - "description": "Collaborator to grant all rights on the newly created gateway." - } - } - }, - "v3CreateOrganizationAPIKeyRequest": { - "type": "object", - "properties": { - "organization_ids": { - "$ref": "#/definitions/v3OrganizationIdentifiers" - }, - "name": { - "type": "string" }, - "rights": { - "type": "array", - "items": { - "$ref": "#/definitions/v3Right" - } + "end_device_version_ids": { + "$ref": "#/definitions/v3EndDeviceVersionIdentifiers", + "description": "End device profile identifiers." } } }, - "v3CreateOrganizationRequest": { + "v3CreateLoginTokenResponse": { "type": "object", "properties": { - "organization": { - "$ref": "#/definitions/v3Organization" - }, - "collaborator": { - "$ref": "#/definitions/v3OrganizationOrUserIdentifiers", - "description": "Collaborator to grant all rights on the newly created application.\nNOTE: It is currently not possible to have organizations collaborating on\nother organizations." + "token": { + "type": "string", + "description": "The token that can be used for logging in as the user.\nThis field is only present if a token was created by an admin user for a non-admin user." } } }, - "v3CreateUserAPIKeyRequest": { + "v3CreateNotificationResponse": { "type": "object", "properties": { - "user_ids": { - "$ref": "#/definitions/v3UserIdentifiers" - }, - "name": { + "id": { "type": "string" - }, - "rights": { - "type": "array", - "items": { - "$ref": "#/definitions/v3Right" - } } } }, @@ -9829,20 +20602,25 @@ "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3EndDeviceIdentifiers" + "$ref": "#/definitions/v3EndDeviceIdentifiers", + "description": "End device identifiers for the cryptographic operation." }, "lorawan_version": { - "$ref": "#/definitions/v3MACVersion" + "$ref": "#/definitions/v3MACVersion", + "description": "LoRaWAN version to use for the cryptographic operation." }, "payload": { "type": "string", - "format": "byte" + "format": "byte", + "description": "Raw input payload." }, "provisioner_id": { - "type": "string" + "type": "string", + "description": "Provisioner that provisioned the end device." }, "provisioning_data": { - "type": "object" + "type": "object", + "description": "Provisioning data for the provisioner." } } }, @@ -9851,7 +20629,8 @@ "properties": { "payload": { "type": "string", - "format": "byte" + "format": "byte", + "description": "Raw output payload." } } }, @@ -9859,15 +20638,13 @@ "type": "object", "properties": { "rx1_dr_offset": { - "type": "integer", - "format": "int64" + "$ref": "#/definitions/v3DataRateOffset" }, "rx2_dr": { "$ref": "#/definitions/v3DataRateIndex" }, "opt_neg": { "type": "boolean", - "format": "boolean", "description": "OptNeg is set if Network Server implements LoRaWAN 1.1 or greater." } } @@ -9878,38 +20655,115 @@ "lora": { "$ref": "#/definitions/v3LoRaDataRate" }, - "fsk": { - "$ref": "#/definitions/v3FSKDataRate" + "fsk": { + "$ref": "#/definitions/v3FSKDataRate" + }, + "lrfhss": { + "$ref": "#/definitions/v3LRFHSSDataRate" + } + } + }, + "v3DataRateIndex": { + "type": "string", + "enum": [ + "DATA_RATE_0", + "DATA_RATE_1", + "DATA_RATE_2", + "DATA_RATE_3", + "DATA_RATE_4", + "DATA_RATE_5", + "DATA_RATE_6", + "DATA_RATE_7", + "DATA_RATE_8", + "DATA_RATE_9", + "DATA_RATE_10", + "DATA_RATE_11", + "DATA_RATE_12", + "DATA_RATE_13", + "DATA_RATE_14", + "DATA_RATE_15" + ], + "default": "DATA_RATE_0" + }, + "v3DataRateIndexValue": { + "type": "object", + "properties": { + "value": { + "$ref": "#/definitions/v3DataRateIndex" + } + } + }, + "v3DataRateOffset": { + "type": "string", + "enum": [ + "DATA_RATE_OFFSET_0", + "DATA_RATE_OFFSET_1", + "DATA_RATE_OFFSET_2", + "DATA_RATE_OFFSET_3", + "DATA_RATE_OFFSET_4", + "DATA_RATE_OFFSET_5", + "DATA_RATE_OFFSET_6", + "DATA_RATE_OFFSET_7" + ], + "default": "DATA_RATE_OFFSET_0" + }, + "v3DataRateOffsetValue": { + "type": "object", + "properties": { + "value": { + "$ref": "#/definitions/v3DataRateOffset" + } + } + }, + "v3DecodeDownlinkResponse": { + "type": "object", + "properties": { + "downlink": { + "$ref": "#/definitions/v3ApplicationDownlink" + } + } + }, + "v3DecodeUplinkResponse": { + "type": "object", + "properties": { + "uplink": { + "$ref": "#/definitions/v3ApplicationUplink" + } + } + }, + "v3DecodedMessagePayload": { + "type": "object", + "properties": { + "data": { + "type": "object" + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + }, + "errors": { + "type": "array", + "items": { + "type": "string" + } } } }, - "v3DataRateIndex": { - "type": "string", - "enum": [ - "DATA_RATE_0", - "DATA_RATE_1", - "DATA_RATE_2", - "DATA_RATE_3", - "DATA_RATE_4", - "DATA_RATE_5", - "DATA_RATE_6", - "DATA_RATE_7", - "DATA_RATE_8", - "DATA_RATE_9", - "DATA_RATE_10", - "DATA_RATE_11", - "DATA_RATE_12", - "DATA_RATE_13", - "DATA_RATE_14", - "DATA_RATE_15" - ], - "default": "DATA_RATE_0" - }, - "v3DataRateIndexValue": { + "v3DevAddrPrefix": { "type": "object", "properties": { - "value": { - "$ref": "#/definitions/v3DataRateIndex" + "dev_addr": { + "type": "string", + "format": "string", + "example": "2600ABCD", + "description": "DevAddr base." + }, + "length": { + "type": "integer", + "format": "int64", + "description": "Number of most significant bits from dev_addr that are used as prefix." } } }, @@ -9935,33 +20789,13 @@ ], "default": "DEVICE_EIRP_8" }, - "v3DownlinkMessage": { + "v3DeviceEIRPValue": { "type": "object", "properties": { - "raw_payload": { - "type": "string", - "format": "byte" - }, - "payload": { - "$ref": "#/definitions/lorawanv3Message" - }, - "end_device_ids": { - "$ref": "#/definitions/v3EndDeviceIdentifiers" - }, - "request": { - "$ref": "#/definitions/v3TxRequest" - }, - "scheduled": { - "$ref": "#/definitions/v3TxSettings" - }, - "correlation_ids": { - "type": "array", - "items": { - "type": "string" - } + "value": { + "$ref": "#/definitions/v3DeviceEIRP" } - }, - "title": "Downlink message from the network to the end device" + } }, "v3DownlinkPath": { "type": "object", @@ -9985,16 +20819,35 @@ "default": "DOWNLINK_PATH_CONSTRAINT_NONE", "description": " - DOWNLINK_PATH_CONSTRAINT_NONE: Indicates that the gateway can be selected for downlink without constraints by the Network Server.\n - DOWNLINK_PATH_CONSTRAINT_PREFER_OTHER: Indicates that the gateway can be selected for downlink only if no other or better gateway can be selected.\n - DOWNLINK_PATH_CONSTRAINT_NEVER: Indicates that this gateway will never be selected for downlink, even if that results in no available downlink path." }, - "v3DownlinkQueueRequest": { + "v3EncodeDownlinkResponse": { "type": "object", "properties": { - "end_device_ids": { - "$ref": "#/definitions/v3EndDeviceIdentifiers" + "downlink": { + "$ref": "#/definitions/v3ApplicationDownlink" + } + } + }, + "v3EncodedMessagePayload": { + "type": "object", + "properties": { + "f_port": { + "type": "integer", + "format": "int64" }, - "downlinks": { + "frm_payload": { + "type": "string", + "format": "byte" + }, + "warnings": { "type": "array", "items": { - "$ref": "#/definitions/v3ApplicationDownlink" + "type": "string" + } + }, + "errors": { + "type": "array", + "items": { + "type": "string" } } } @@ -10073,12 +20926,10 @@ }, "supports_class_b": { "type": "boolean", - "format": "boolean", "description": "Whether the device supports class B.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." }, "supports_class_c": { "type": "boolean", - "format": "boolean", "description": "Whether the device supports class C.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." }, "lorawan_version": { @@ -10105,12 +20956,10 @@ }, "supports_join": { "type": "boolean", - "format": "boolean", "description": "The device supports join (it's OTAA).\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." }, "resets_join_nonces": { "type": "boolean", - "format": "boolean", "description": "Whether the device resets the join and dev nonces (not LoRaWAN compliant). Stored in Join Server.\nCopied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any." }, "root_keys": { @@ -10119,7 +20968,8 @@ }, "net_id": { "type": "string", - "format": "byte", + "format": "string", + "example": "000013", "description": "Home NetID. Stored in Join Server." }, "mac_settings": { @@ -10189,27 +21039,6 @@ "format": "int32", "description": "Demodulation signal-to-noise ratio (dB).\nReceived via the DevStatus MAC command at last_dev_status_received_at.\nStored in Network Server." }, - "recent_adr_uplinks": { - "type": "array", - "items": { - "$ref": "#/definitions/v3UplinkMessage" - }, - "title": "Recent uplink messages with ADR bit set to 1 sorted by time. Stored in Network Server.\nThe field is reset each time an uplink message carrying MACPayload is received with ADR bit set to 0.\nThe number of messages stored is in the range [0,20];" - }, - "recent_uplinks": { - "type": "array", - "items": { - "$ref": "#/definitions/v3UplinkMessage" - }, - "description": "Recent uplink messages sorted by time. Stored in Network Server.\nThe number of messages stored may depend on configuration." - }, - "recent_downlinks": { - "type": "array", - "items": { - "$ref": "#/definitions/v3DownlinkMessage" - }, - "description": "Recent downlink messages sorted by time. Stored in Network Server.\nThe number of messages stored may depend on configuration." - }, "queued_application_downlinks": { "type": "array", "items": { @@ -10231,22 +21060,29 @@ }, "multicast": { "type": "boolean", - "format": "boolean", "description": "Indicates whether this device represents a multicast group." }, "claim_authentication_code": { "$ref": "#/definitions/v3EndDeviceAuthenticationCode", - "description": "Authentication code to claim ownership of the end device. Stored in Join Server." + "description": "Authentication code to claim ownership of the end device.\nFrom TTS v3.21.0 this field is stored in the Identity Server.\nFor TTS versions \u003c 3.21.0, this field is stored in the Join Server.\nThe value stored on the Identity Server takes precedence." }, "skip_payload_crypto": { "type": "boolean", - "format": "boolean", "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field is deprecated, use skip_payload_crypto_override instead." }, "skip_payload_crypto_override": { "type": "boolean", - "format": "boolean", "description": "Skip decryption of uplink payloads and encryption of downlink payloads.\nThis field overrides the application-level setting." + }, + "activated_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when the device has been activated. Stored in the Entity Registry.\nThis field is set by the Application Server when an end device sends\nits first uplink.\nThe Application Server will use the field in order to avoid repeated\ncalls to the Entity Registry.\nThe field cannot be unset once set." + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "description": "Timestamp when a device uplink has been last observed.\nThis field is set by the Application Server and stored in the Identity Server." } }, "description": "Defines an End Device registration and its state on the network.\nThe persistence of the EndDevice is divided between the Network Server, Application Server and Join Server.\nSDKs are responsible for combining (if desired) the three." @@ -10268,6 +21104,48 @@ }, "description": "Authentication code for end devices." }, + "v3EndDeviceBrand": { + "type": "object", + "properties": { + "brand_id": { + "type": "string", + "description": "Brand identifier, as specified in the Device Repository." + }, + "name": { + "type": "string", + "description": "Brand name." + }, + "private_enterprise_number": { + "type": "integer", + "format": "int64", + "description": "Private Enterprise Number (PEN) assigned by IANA." + }, + "organization_unique_identifiers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Organization Unique Identifiers (OUI) assigned by IEEE." + }, + "lora_alliance_vendor_id": { + "type": "integer", + "format": "int64", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005." + }, + "website": { + "type": "string", + "description": "Brand website URL." + }, + "email": { + "type": "string", + "description": "Contact email address." + }, + "logo": { + "type": "string", + "description": "Path to brand logo." + } + } + }, "v3EndDeviceIdentifiers": { "type": "object", "properties": { @@ -10279,21 +21157,132 @@ }, "dev_eui": { "type": "string", - "format": "byte", + "format": "string", + "example": "70B3D57ED000ABCD", "description": "The LoRaWAN DevEUI." }, "join_eui": { "type": "string", - "format": "byte", + "format": "string", + "example": "70B3D57ED000ABCD", "description": "The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices)." }, "dev_addr": { "type": "string", - "format": "byte", + "format": "string", + "example": "2600ABCD", "description": "The LoRaWAN DevAddr." } } }, + "v3EndDeviceModel": { + "type": "object", + "properties": { + "brand_id": { + "type": "string", + "description": "Brand identifier, as defined in the Device Repository." + }, + "model_id": { + "type": "string", + "description": "Model identifier, as defined in the Device Repository." + }, + "name": { + "type": "string", + "description": "Model name, as defined in the Device Repository." + }, + "description": { + "type": "string", + "description": "Model description." + }, + "hardware_versions": { + "type": "array", + "items": { + "$ref": "#/definitions/EndDeviceModelHardwareVersion" + }, + "description": "Available hardware versions." + }, + "firmware_versions": { + "type": "array", + "items": { + "$ref": "#/definitions/EndDeviceModelFirmwareVersion" + }, + "description": "Available firmware versions." + }, + "sensors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of sensors included in the device." + }, + "dimensions": { + "$ref": "#/definitions/EndDeviceModelDimensions", + "description": "Device dimensions." + }, + "weight": { + "type": "number", + "format": "float", + "description": "Device weight (gram)." + }, + "battery": { + "$ref": "#/definitions/EndDeviceModelBattery", + "description": "Device battery information." + }, + "operating_conditions": { + "$ref": "#/definitions/EndDeviceModelOperatingConditions", + "description": "Device operating conditions." + }, + "ip_code": { + "type": "string", + "description": "Device IP rating code." + }, + "key_provisioning": { + "type": "array", + "items": { + "$ref": "#/definitions/v3KeyProvisioning" + }, + "description": "Supported key provisioning methods." + }, + "key_security": { + "$ref": "#/definitions/v3KeySecurity", + "description": "Device key security." + }, + "photos": { + "$ref": "#/definitions/EndDeviceModelPhotos", + "description": "Device photos." + }, + "videos": { + "$ref": "#/definitions/EndDeviceModelVideos", + "description": "Device videos." + }, + "product_url": { + "type": "string", + "description": "Device information page URL." + }, + "datasheet_url": { + "type": "string", + "description": "Device datasheet URL." + }, + "resellers": { + "type": "array", + "items": { + "$ref": "#/definitions/EndDeviceModelReseller" + }, + "description": "Reseller URLs." + }, + "compliances": { + "$ref": "#/definitions/EndDeviceModelCompliances", + "description": "List of standards the device is compliant with." + }, + "additional_radios": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of any additional radios included in the device." + } + } + }, "v3EndDeviceTemplate": { "type": "object", "properties": { @@ -10301,7 +21290,7 @@ "$ref": "#/definitions/v3EndDevice" }, "field_mask": { - "$ref": "#/definitions/protobufFieldMask" + "type": "string" }, "mapping_key": { "type": "string" @@ -10350,6 +21339,22 @@ }, "firmware_version": { "type": "string" + }, + "band_id": { + "type": "string" + }, + "vendor_id": { + "type": "integer", + "format": "int64", + "description": "VendorID managed by the LoRa Alliance, as defined in TR005." + }, + "vendor_profile_id": { + "type": "integer", + "format": "int64", + "description": "ID of the LoRaWAN end device profile assigned by the vendor." + }, + "serial_number": { + "type": "string" } }, "description": "Identifies an end device model with version information." @@ -10378,7 +21383,7 @@ "$ref": "#/definitions/v3EndDeviceIdentifiers" }, "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" }, "organization_ids": { "$ref": "#/definitions/v3OrganizationIdentifiers" @@ -10435,43 +21440,63 @@ "type": "object", "properties": { "name": { - "type": "string" + "type": "string", + "description": "Name of the event. This can be used to find the (localized) event description." }, "time": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "Time at which the event was triggered." }, "identifiers": { "type": "array", "items": { "$ref": "#/definitions/v3EntityIdentifiers" - } + }, + "description": "Identifiers of the entity (or entities) involved." }, "data": { - "$ref": "#/definitions/protobufAny" + "$ref": "#/definitions/protobufAny", + "description": "Optional data attached to the event." }, "correlation_ids": { "type": "array", "items": { "type": "string" - } + }, + "description": "Correlation IDs can be used to find related events and actions such as API calls." }, "origin": { - "type": "string" + "type": "string", + "description": "The origin of the event. Typically the hostname of the server that created it." }, "context": { "type": "object", "additionalProperties": { "type": "string", "format": "byte" - } + }, + "description": "Event context, internal use only." }, "visibility": { "$ref": "#/definitions/v3Rights", "description": "The event will be visible to a caller that has any of these rights." }, "authentication": { - "$ref": "#/definitions/EventAuthentication" + "$ref": "#/definitions/EventAuthentication", + "description": "Details on the authentication provided by the caller that triggered this event." + }, + "remote_ip": { + "type": "string", + "description": "The IP address of the caller that triggered this event." + }, + "user_agent": { + "type": "string", + "description": "The IP address of the caller that triggered this event." + }, + "unique_id": { + "type": "string", + "description": "The unique identifier of the event, assigned on creation." } } }, @@ -10479,24 +21504,19 @@ "type": "object", "properties": { "adr": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "adr_ack_req": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "ack": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "f_pending": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "class_b": { - "type": "boolean", - "format": "boolean" + "type": "boolean" } } }, @@ -10505,7 +21525,8 @@ "properties": { "dev_addr": { "type": "string", - "format": "byte" + "format": "string", + "example": "2600ABCD" }, "f_ctrl": { "$ref": "#/definitions/v3FCtrl" @@ -10530,6 +21551,17 @@ } } }, + "v3FindRelatedEventsResponse": { + "type": "object", + "properties": { + "events": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Event" + } + } + } + }, "v3FrequencyPlanDescription": { "type": "object", "properties": { @@ -10550,25 +21582,44 @@ } } }, + "v3FrequencyValue": { + "type": "object", + "properties": { + "value": { + "type": "string", + "format": "uint64" + } + } + }, "v3Gateway": { "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" + "$ref": "#/definitions/lorawanv3GatewayIdentifiers", + "description": "The identifiers of the gateway. These are public and can be seen by any authenticated user in the network." }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the gateway was created. This information is public and can be seen by any authenticated user in the network." }, "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the gateway was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the gateway was deleted. This information is public and can be seen by any authenticated user in the network." }, "name": { - "type": "string" + "type": "string", + "description": "The name of the gateway. This information is public and can be seen by any authenticated user in the network." }, "description": { - "type": "string" + "type": "string", + "description": "A description for the gateway. This information is public and can be seen by any authenticated user in the network." }, "attributes": { "type": "object", @@ -10582,57 +21633,59 @@ "items": { "$ref": "#/definitions/v3ContactInfo" }, - "description": "Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway." + "description": "Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" }, "version_ids": { "$ref": "#/definitions/v3GatewayVersionIdentifiers" }, "gateway_server_address": { "type": "string", - "description": "The address of the Gateway Server to connect to.\nThe typical format of the address is \"host:port\". If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." + "description": "The address of the Gateway Server to connect to.\nThis information is public and can be seen by any authenticated user in the network if status_public is true.\nThe typical format of the address is \"scheme://host:port\". The scheme is optional. If the port is omitted,\nthe normal port inference (with DNS lookup, otherwise defaults) is used.\nThe connection shall be established with transport layer security (TLS).\nCustom certificate authorities may be configured out-of-band." }, "auto_update": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "update_channel": { "type": "string" }, "frequency_plan_id": { "type": "string", - "description": "Frequency plan ID of the gateway.\nThis equals the first element of the frequency_plan_ids field." + "description": "Frequency plan ID of the gateway.\nThis information is public and can be seen by any authenticated user in the network.\nDEPRECATED: use frequency_plan_ids.\nThis equals the first element of the frequency_plan_ids field." }, "frequency_plan_ids": { "type": "array", "items": { "type": "string" }, - "description": "Frequency plan IDs of the gateway.\nThe first element equals the frequency_plan_id field." + "description": "Frequency plan IDs of the gateway.\nThis information is public and can be seen by any authenticated user in the network.\nThe first element equals the frequency_plan_id field." }, "antennas": { "type": "array", "items": { "$ref": "#/definitions/v3GatewayAntenna" - } + }, + "description": "Antennas of the gateway. Location information of the antennas is public and can be seen by any authenticated user in the network if location_public=true." }, "status_public": { "type": "boolean", - "format": "boolean", "description": "The status of this gateway may be publicly displayed." }, "location_public": { "type": "boolean", - "format": "boolean", "description": "The location of this gateway may be publicly displayed." }, "schedule_downlink_late": { "type": "boolean", - "format": "boolean", "description": "Enable server-side buffering of downlink messages. This is recommended for gateways using the Semtech UDP Packet\nForwarder v2.x or older, as it does not feature a just-in-time queue. If enabled, the Gateway Server schedules the\ndownlink message late to the gateway so that it does not overwrite previously scheduled downlink messages that have\nnot been transmitted yet." }, "enforce_duty_cycle": { "type": "boolean", - "format": "boolean", "description": "Enforcing gateway duty cycle is recommended for all gateways to respect spectrum regulations. Disable enforcing the\nduty cycle only in controlled research and development environments." }, "downlink_path_constraint": { @@ -10644,8 +21697,33 @@ }, "update_location_from_status": { "type": "boolean", - "format": "boolean", - "title": "update the location of this gateway from status messages" + "description": "Update the location of this gateway from status messages. This only works for gateways connecting with authentication; gateways connected over UDP are not supported." + }, + "lbs_lns_secret": { + "$ref": "#/definitions/v3Secret", + "description": "The LoRa Basics Station LNS secret.\nThis is either an auth token (such as an API Key) or a TLS private certificate.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value." + }, + "claim_authentication_code": { + "$ref": "#/definitions/v3GatewayClaimAuthenticationCode", + "description": "The authentication code for gateway claiming.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value.\nThe entire field must be used in RPCs since sub-fields are validated wrt to each other. Direct selection/update of sub-fields only are not allowed.\nUse the top level field mask `claim_authentication_code` even when updating single fields." + }, + "target_cups_uri": { + "type": "string", + "description": "CUPS URI for LoRa Basics Station CUPS redirection.\nThe CUPS Trust field will be automatically fetched from the cert chain presented by the target server." + }, + "target_cups_key": { + "$ref": "#/definitions/v3Secret", + "description": "CUPS Key for LoRa Basics Station CUPS redirection.\nIf redirecting to another instance of TTS, use the CUPS API Key for the gateway on the target instance.\nRequires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value." + }, + "require_authenticated_connection": { + "type": "boolean", + "description": "Require an authenticated gateway connection. This prevents the gateway from using the UDP protocol and requires authentication when using other protocols." + }, + "lrfhss": { + "$ref": "#/definitions/GatewayLRFHSS" + }, + "disable_packet_broker_forwarding": { + "type": "boolean" } }, "description": "Gateway is the message that defines a gateway on the network." @@ -10656,7 +21734,7 @@ "gain": { "type": "number", "format": "float", - "description": "gain is the antenna gain relative to this gateway, in dBi." + "description": "Antenna gain relative to the gateway, in dBi." }, "location": { "$ref": "#/definitions/lorawanv3Location", @@ -10667,6 +21745,9 @@ "additionalProperties": { "type": "string" } + }, + "placement": { + "$ref": "#/definitions/v3GatewayAntennaPlacement" } }, "description": "GatewayAntenna is the message that defines a gateway antenna." @@ -10675,7 +21756,7 @@ "type": "object", "properties": { "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" + "$ref": "#/definitions/lorawanv3GatewayIdentifiers" }, "antenna_index": { "type": "integer", @@ -10683,6 +21764,32 @@ } } }, + "v3GatewayAntennaPlacement": { + "type": "string", + "enum": [ + "PLACEMENT_UNKNOWN", + "INDOOR", + "OUTDOOR" + ], + "default": "PLACEMENT_UNKNOWN" + }, + "v3GatewayClaimAuthenticationCode": { + "type": "object", + "properties": { + "secret": { + "$ref": "#/definitions/v3Secret" + }, + "valid_from": { + "type": "string", + "format": "date-time" + }, + "valid_to": { + "type": "string", + "format": "date-time" + } + }, + "description": "Authentication code for claiming gateways." + }, "v3GatewayConnectionStats": { "type": "object", "properties": { @@ -10690,6 +21797,10 @@ "type": "string", "format": "date-time" }, + "disconnected_at": { + "type": "string", + "format": "date-time" + }, "protocol": { "type": "string" }, @@ -10725,6 +21836,10 @@ "$ref": "#/definitions/GatewayConnectionStatsSubBand" }, "description": "Statistics for each sub band." + }, + "gateway_remote_address": { + "$ref": "#/definitions/v3GatewayRemoteAddress", + "description": "Gateway Remote Address." } }, "description": "Connection stats as monitored by the Gateway Server." @@ -10733,31 +21848,17 @@ "type": "object", "properties": { "downlink_message": { - "$ref": "#/definitions/v3DownlinkMessage", + "$ref": "#/definitions/lorawanv3DownlinkMessage", "description": "DownlinkMessage for the gateway." } }, "description": "GatewayDown contains downlink messages for the gateway." }, - "v3GatewayIdentifiers": { - "type": "object", - "properties": { - "gateway_id": { - "type": "string" - }, - "eui": { - "type": "string", - "format": "byte", - "description": "Secondary identifier, which can only be used in specific requests." - } - } - }, "v3GatewayRadio": { "type": "object", "properties": { "enable": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "chip_type": { "type": "string" @@ -10775,6 +21876,15 @@ } } }, + "v3GatewayRemoteAddress": { + "type": "object", + "properties": { + "ip": { + "type": "string" + } + }, + "description": "Remote Address of the Gateway, as seen by the Gateway Server." + }, "v3GatewayStatus": { "type": "object", "properties": { @@ -10857,21 +21967,26 @@ "properties": { "dev_addr": { "type": "string", - "format": "byte" + "format": "string", + "example": "2600ABCD" } - } + }, + "description": "Response of GenerateDevAddr." }, "v3GenerateEndDeviceQRCodeRequest": { "type": "object", "properties": { "format_id": { - "type": "string" + "type": "string", + "description": "QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service." }, "end_device": { - "$ref": "#/definitions/v3EndDevice" + "$ref": "#/definitions/v3EndDevice", + "description": "End device to use as input to generate the QR code." }, "image": { - "$ref": "#/definitions/GenerateEndDeviceQRCodeRequestImage" + "$ref": "#/definitions/GenerateEndDeviceQRCodeRequestImage", + "description": "If set, the server will render the QR code image according to these settings." } } }, @@ -10879,7 +21994,8 @@ "type": "object", "properties": { "text": { - "type": "string" + "type": "string", + "description": "Text representation of the QR code contents." }, "image": { "$ref": "#/definitions/v3Picture", @@ -10887,6 +22003,35 @@ } } }, + "v3GetAsConfigurationResponse": { + "type": "object", + "properties": { + "configuration": { + "$ref": "#/definitions/v3AsConfiguration" + } + } + }, + "v3GetClaimStatusResponse": { + "type": "object", + "properties": { + "end_device_ids": { + "$ref": "#/definitions/v3EndDeviceIdentifiers" + }, + "home_net_id": { + "type": "string", + "format": "string", + "example": "000013" + }, + "home_ns_id": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + }, + "vendor_specific": { + "$ref": "#/definitions/GetClaimStatusResponseVendorSpecific" + } + } + }, "v3GetCollaboratorResponse": { "type": "object", "properties": { @@ -10901,6 +22046,64 @@ } } }, + "v3GetDefaultJoinEUIResponse": { + "type": "object", + "properties": { + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + } + } + }, + "v3GetDeviceAdressPrefixesResponse": { + "type": "object", + "properties": { + "dev_addr_prefixes": { + "type": "array", + "example": [ + "2600AB00/24" + ], + "items": { + "type": "string", + "format": "byte" + } + } + } + }, + "v3GetGatewayConfigurationResponse": { + "type": "object", + "properties": { + "contents": { + "type": "string", + "format": "byte" + } + } + }, + "v3GetInfoByJoinEUIRequest": { + "type": "object", + "properties": { + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + } + } + }, + "v3GetInfoByJoinEUIResponse": { + "type": "object", + "properties": { + "join_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" + }, + "supports_claiming": { + "type": "boolean", + "description": "If set, this Join EUI is available for claiming on one of the configured Join Servers." + } + } + }, "v3GetIsConfigurationResponse": { "type": "object", "properties": { @@ -10909,6 +22112,40 @@ } } }, + "v3GetNetIDResponse": { + "type": "object", + "properties": { + "net_id": { + "type": "string", + "format": "string", + "example": "000013" + } + } + }, + "v3GetPhyVersionsResponse": { + "type": "object", + "properties": { + "version_info": { + "type": "array", + "items": { + "$ref": "#/definitions/GetPhyVersionsResponseVersionInfo" + } + } + } + }, + "v3GetStoredApplicationUpCountResponse": { + "type": "object", + "properties": { + "count": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int64" + }, + "description": "Number of stored messages by end device ID." + } + } + }, "v3GrantType": { "type": "string", "enum": [ @@ -10944,6 +22181,22 @@ }, "user_rights": { "$ref": "#/definitions/IsConfigurationUserRights" + }, + "user_login": { + "$ref": "#/definitions/IsConfigurationUserLogin" + }, + "admin_rights": { + "$ref": "#/definitions/IsConfigurationAdminRights" + } + } + }, + "v3IssueDevEUIResponse": { + "type": "object", + "properties": { + "dev_eui": { + "type": "string", + "format": "string", + "example": "70B3D57ED000ABCD" } } }, @@ -10956,15 +22209,18 @@ }, "join_nonce": { "type": "string", - "format": "byte" + "format": "string", + "example": "ABCDEF" }, "net_id": { "type": "string", - "format": "byte" + "format": "string", + "example": "000013" }, "dev_addr": { "type": "string", - "format": "byte" + "format": "string", + "example": "2600ABCD" }, "dl_settings": { "$ref": "#/definitions/v3DLSettings" @@ -10982,7 +22238,8 @@ "properties": { "join_eui": { "type": "string", - "format": "byte" + "format": "string", + "example": "70B3D57ED000ABCD" }, "length": { "type": "integer", @@ -11001,62 +22258,36 @@ } } }, - "v3JoinRequest": { - "type": "object", - "properties": { - "raw_payload": { - "type": "string", - "format": "byte" - }, - "payload": { - "$ref": "#/definitions/lorawanv3Message" - }, - "dev_addr": { - "type": "string", - "format": "byte" - }, - "selected_mac_version": { - "$ref": "#/definitions/v3MACVersion" - }, - "net_id": { - "type": "string", - "format": "byte" - }, - "downlink_settings": { - "$ref": "#/definitions/v3DLSettings" - }, - "rx_delay": { - "$ref": "#/definitions/v3RxDelay" - }, - "cf_list": { - "$ref": "#/definitions/v3CFList", - "description": "Optional CFList." - }, - "correlation_ids": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "v3JoinRequestPayload": { "type": "object", "properties": { "join_eui": { "type": "string", - "format": "byte" + "format": "string", + "example": "70B3D57ED000ABCD" }, "dev_eui": { "type": "string", - "format": "byte" + "format": "string", + "example": "70B3D57ED000ABCD" }, "dev_nonce": { "type": "string", - "format": "byte" + "format": "string", + "example": "ABCD" } } }, + "v3JoinRequestType": { + "type": "string", + "enum": [ + "REJOIN_CONTEXT", + "REJOIN_SESSION", + "REJOIN_KEYS", + "JOIN" + ], + "default": "REJOIN_CONTEXT" + }, "v3JoinResponse": { "type": "object", "properties": { @@ -11083,7 +22314,8 @@ "properties": { "key": { "type": "string", - "format": "byte", + "format": "string", + "example": "0123456789ABCDEF0123456789ABCDEF", "description": "The unencrypted AES key." }, "kek_label": { @@ -11096,6 +22328,78 @@ } } }, + "v3KeyProvisioning": { + "type": "string", + "enum": [ + "KEY_PROVISIONING_UNKNOWN", + "KEY_PROVISIONING_CUSTOM", + "KEY_PROVISIONING_JOIN_SERVER", + "KEY_PROVISIONING_MANIFEST" + ], + "default": "KEY_PROVISIONING_UNKNOWN", + "description": " - KEY_PROVISIONING_UNKNOWN: Unknown Key Provisioning.\n - KEY_PROVISIONING_CUSTOM: Custom Key Provisioning.\n - KEY_PROVISIONING_JOIN_SERVER: Key Provisioning from the Global Join Server.\n - KEY_PROVISIONING_MANIFEST: Key Provisioning from Manifest." + }, + "v3KeySecurity": { + "type": "string", + "enum": [ + "KEY_SECURITY_UNKNOWN", + "KEY_SECURITY_NONE", + "KEY_SECURITY_READ_PROTECTED", + "KEY_SECURITY_SECURE_ELEMENT" + ], + "default": "KEY_SECURITY_UNKNOWN", + "description": " - KEY_SECURITY_UNKNOWN: Unknown key security.\n - KEY_SECURITY_NONE: No key security.\n - KEY_SECURITY_READ_PROTECTED: Read Protected key security.\n - KEY_SECURITY_SECURE_ELEMENT: Key security using the Security Element." + }, + "v3LRFHSSDataRate": { + "type": "object", + "properties": { + "modulation_type": { + "type": "integer", + "format": "int64" + }, + "operating_channel_width": { + "type": "integer", + "format": "int64", + "description": "Operating Channel Width (Hz)." + }, + "coding_rate": { + "type": "string" + } + } + }, + "v3ListBandsResponse": { + "type": "object", + "properties": { + "descriptions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ListBandsResponseVersionedBandDescription" + } + } + } + }, + "v3ListEndDeviceBrandsResponse": { + "type": "object", + "properties": { + "brands": { + "type": "array", + "items": { + "$ref": "#/definitions/v3EndDeviceBrand" + } + } + } + }, + "v3ListEndDeviceModelsResponse": { + "type": "object", + "properties": { + "models": { + "type": "array", + "items": { + "$ref": "#/definitions/v3EndDeviceModel" + } + } + } + }, "v3ListFrequencyPlansResponse": { "type": "object", "properties": { @@ -11107,6 +22411,17 @@ } } }, + "v3ListNotificationsResponse": { + "type": "object", + "properties": { + "notifications": { + "type": "array", + "items": { + "$ref": "#/definitions/v3Notification" + } + } + } + }, "v3LoRaDataRate": { "type": "object", "properties": { @@ -11118,6 +22433,9 @@ "spreading_factor": { "type": "integer", "format": "int64" + }, + "coding_rate": { + "type": "string" } } }, @@ -11303,8 +22621,7 @@ "description": "Rx1 delay (Rx2 delay is Rx1 delay + 1 second)." }, "rx1_data_rate_offset": { - "type": "integer", - "format": "int64", + "$ref": "#/definitions/v3DataRateOffset", "description": "Data rate offset for Rx1." }, "rx2_data_rate_index": { @@ -11350,13 +22667,11 @@ "description": "Configured uplink channels and optionally Rx1 frequency." }, "uplink_dwell_time": { - "type": "boolean", - "format": "boolean", + "$ref": "#/definitions/lorawanv3BoolValue", "description": "Whether uplink dwell time is set (400ms).\nIf this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it)." }, "downlink_dwell_time": { - "type": "boolean", - "format": "boolean", + "$ref": "#/definitions/lorawanv3BoolValue", "description": "Whether downlink dwell time is set (400ms).\nIf this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it)." }, "adr_ack_limit_exponent": { @@ -11397,35 +22712,10 @@ }, "enable_uplink": { "type": "boolean", - "format": "boolean", "description": "Channel can be used by device for uplink." } } }, - "v3MACPayload": { - "type": "object", - "properties": { - "f_hdr": { - "$ref": "#/definitions/v3FHDR" - }, - "f_port": { - "type": "integer", - "format": "int64" - }, - "frm_payload": { - "type": "string", - "format": "byte" - }, - "decoded_payload": { - "type": "object" - }, - "full_f_cnt": { - "type": "integer", - "format": "int64", - "description": "Full 32-bit FCnt value. Used internally by Network Server." - } - } - }, "v3MACSettings": { "type": "object", "properties": { @@ -11442,13 +22732,11 @@ "description": "Data rate index of the class B ping slot.\nIf unset, the default value from Network Server configuration will be used." }, "ping_slot_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3ZeroableFrequencyValue", "description": "Frequency of the class B ping slot (Hz).\nIf unset, the default value from Network Server configuration will be used." }, "beacon_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3ZeroableFrequencyValue", "description": "Frequency of the class B beacon (Hz).\nIf unset, the default value from Network Server configuration will be used." }, "class_c_timeout": { @@ -11460,8 +22748,7 @@ "description": "Class A Rx1 delay.\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." }, "rx1_data_rate_offset": { - "type": "integer", - "format": "int64", + "$ref": "#/definitions/v3DataRateOffsetValue", "description": "Rx1 data rate offset.\nIf unset, the default value from Network Server configuration will be used." }, "rx2_data_rate_index": { @@ -11469,8 +22756,7 @@ "description": "Data rate index for Rx2.\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." }, "rx2_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3FrequencyValue", "description": "Frequency for Rx2 (Hz).\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." }, "factory_preset_frequencies": { @@ -11486,23 +22772,20 @@ "description": "Maximum uplink duty cycle (of all channels)." }, "supports_32_bit_f_cnt": { - "type": "boolean", - "format": "boolean", + "$ref": "#/definitions/lorawanv3BoolValue", "description": "Whether the device supports 32-bit frame counters.\nIf unset, the default value from Network Server configuration will be used." }, "use_adr": { - "type": "boolean", - "format": "boolean", - "description": "Whether the Network Server should use ADR for the device.\nIf unset, the default value from Network Server configuration will be used." + "$ref": "#/definitions/lorawanv3BoolValue", + "description": "Whether the Network Server should use ADR for the device.\nThis field is deprecated, use adr_settings instead." }, "adr_margin": { "type": "number", "format": "float", - "description": "The ADR margin tells the network server how much margin it should add in ADR requests.\nA bigger margin is less efficient, but gives a better chance of successful reception.\nIf unset, the default value from Network Server configuration will be used." + "description": "The ADR margin (dB) tells the network server how much margin it should add in ADR requests.\nA bigger margin is less efficient, but gives a better chance of successful reception.\nThis field is deprecated, use adr_settings.dynamic.margin instead." }, "resets_f_cnt": { - "type": "boolean", - "format": "boolean", + "$ref": "#/definitions/lorawanv3BoolValue", "description": "Whether the device resets the frame counters (not LoRaWAN compliant).\nIf unset, the default value from Network Server configuration will be used." }, "status_time_periodicity": { @@ -11519,8 +22802,7 @@ "description": "The Rx1 delay Network Server should configure device to use via MAC commands or Join-Accept.\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." }, "desired_rx1_data_rate_offset": { - "type": "integer", - "format": "int64", + "$ref": "#/definitions/v3DataRateOffsetValue", "description": "The Rx1 data rate offset Network Server should configure device to use via MAC commands or Join-Accept.\nIf unset, the default value from Network Server configuration will be used." }, "desired_rx2_data_rate_index": { @@ -11528,8 +22810,7 @@ "description": "The Rx2 data rate index Network Server should configure device to use via MAC commands or Join-Accept.\nIf unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used." }, "desired_rx2_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3FrequencyValue", "description": "The Rx2 frequency index Network Server should configure device to use via MAC commands.\nIf unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used." }, "desired_max_duty_cycle": { @@ -11549,14 +22830,36 @@ "description": "The data rate index of the class B ping slot Network Server should configure device to use via MAC commands.\nIf unset, the default value from Network Server configuration will be used." }, "desired_ping_slot_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3ZeroableFrequencyValue", "description": "The frequency of the class B ping slot (Hz) Network Server should configure device to use via MAC commands.\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." }, "desired_beacon_frequency": { - "type": "string", - "format": "uint64", + "$ref": "#/definitions/v3ZeroableFrequencyValue", "description": "The frequency of the class B beacon (Hz) Network Server should configure device to use via MAC commands.\nIf unset, the default value from Network Server configuration will be used." + }, + "desired_max_eirp": { + "$ref": "#/definitions/v3DeviceEIRPValue", + "description": "Maximum EIRP (dBm).\nIf unset, the default value from regional parameters specification will be used." + }, + "class_b_c_downlink_interval": { + "type": "string", + "description": "The minimum duration passed before a network-initiated(e.g. Class B or C) downlink following an arbitrary downlink." + }, + "uplink_dwell_time": { + "$ref": "#/definitions/lorawanv3BoolValue", + "description": "Whether uplink dwell time is set (400ms).\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." + }, + "downlink_dwell_time": { + "$ref": "#/definitions/lorawanv3BoolValue", + "description": "Whether downlink dwell time is set (400ms).\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." + }, + "adr": { + "$ref": "#/definitions/v3ADRSettings", + "description": "Adaptive Data Rate settings.\nIf unset, the default value from Network Server configuration or regional parameters specification will be used." + }, + "schedule_downlinks": { + "$ref": "#/definitions/lorawanv3BoolValue", + "description": "Whether or not downlink messages should be scheduled.\nThis option can be used in order to disable any downlink interaction with the end device. It will affect all types\nof downlink messages: data and MAC downlinks, and join accepts." } } }, @@ -11616,25 +22919,24 @@ "description": "Queued join-accept.\nSet each time a (re-)join request accept is received from Join Server and removed each time a downlink is scheduled." }, "pending_join_request": { - "$ref": "#/definitions/v3JoinRequest", - "description": "Pending join request.\nSet each time a join accept is scheduled and removed each time an uplink is received from the device." + "$ref": "#/definitions/v3MACStateJoinRequest", + "description": "Pending join request.\nSet each time a join-accept is scheduled and removed each time an uplink is received from the device." }, "rx_windows_available": { "type": "boolean", - "format": "boolean", "description": "Whether or not Rx windows are expected to be open.\nSet to true every time an uplink is received.\nSet to false every time a successful downlink scheduling attempt is made." }, "recent_uplinks": { "type": "array", "items": { - "$ref": "#/definitions/v3UplinkMessage" + "$ref": "#/definitions/v3MACStateUplinkMessage" }, "description": "Recent data uplink messages sorted by time.\nThe number of messages stored may depend on configuration." }, "recent_downlinks": { "type": "array", "items": { - "$ref": "#/definitions/v3DownlinkMessage" + "$ref": "#/definitions/v3MACStateDownlinkMessage" }, "description": "Recent data downlink messages sorted by time.\nThe number of messages stored may depend on configuration." }, @@ -11670,9 +22972,89 @@ "type": "string", "format": "date-time", "description": "Time when the last downlink message was scheduled." + }, + "rejected_data_rate_ranges": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/MACStateDataRateRanges" + }, + "description": "Data rate ranges rejected by the device per frequency." + }, + "last_adr_change_f_cnt_up": { + "type": "integer", + "format": "int64", + "description": "Frame counter of uplink, which confirmed the last ADR parameter change." + }, + "recent_mac_command_identifiers": { + "type": "array", + "items": { + "$ref": "#/definitions/v3MACCommandIdentifier" + }, + "description": "MAC command identifiers sent by the end device in the last received uplink.\nThe Network Server may choose to store only certain types of MAC\ncommand identifiers in the underlying implementation." + } + }, + "description": "MACState represents the state of MAC layer of the device.\nMACState is reset on each join for OTAA or ResetInd for ABP devices.\nThis is used internally by the Network Server." + }, + "v3MACStateDownlinkMessage": { + "type": "object", + "properties": { + "payload": { + "$ref": "#/definitions/MACStateDownlinkMessageMessage" + }, + "correlation_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "description": "A minimal DownlinkMessage definition which is binary compatible with the top level DownlinkMessage message.\nUsed for type safe recent downlink storage." + }, + "v3MACStateJoinRequest": { + "type": "object", + "properties": { + "downlink_settings": { + "$ref": "#/definitions/v3DLSettings" + }, + "rx_delay": { + "$ref": "#/definitions/v3RxDelay" + }, + "cf_list": { + "$ref": "#/definitions/v3CFList" + } + } + }, + "v3MACStateUplinkMessage": { + "type": "object", + "properties": { + "payload": { + "$ref": "#/definitions/lorawanv3Message" + }, + "settings": { + "$ref": "#/definitions/MACStateUplinkMessageTxSettings" + }, + "rx_metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/MACStateUplinkMessageRxMetadata" + } + }, + "received_at": { + "type": "string", + "format": "date-time" + }, + "correlation_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "device_channel_index": { + "type": "integer", + "format": "int64" } }, - "description": "MACState represents the state of MAC layer of the device.\nMACState is reset on each join for OTAA or ResetInd for ABP devices.\nThis is used internally by the Network Server and is read only." + "description": "A minimal UplinkMessage definition which is binary compatible with the top level UplinkMessage message.\nUsed for type safe recent uplink storage." }, "v3MACVersion": { "type": "string", @@ -11687,17 +23069,6 @@ ], "default": "MAC_UNKNOWN" }, - "v3MHDR": { - "type": "object", - "properties": { - "m_type": { - "$ref": "#/definitions/v3MType" - }, - "major": { - "$ref": "#/definitions/v3Major" - } - } - }, "v3MQTTConnectionInfo": { "type": "object", "properties": { @@ -11737,6 +23108,78 @@ ], "default": "LORAWAN_R1" }, + "v3MessagePayloadDecoder": { + "type": "object", + "properties": { + "formatter": { + "$ref": "#/definitions/v3PayloadFormatter", + "description": "Payload formatter type." + }, + "formatter_parameter": { + "type": "string", + "description": "Parameter for the formatter, must be set together." + }, + "codec_id": { + "type": "string" + }, + "examples": { + "type": "array", + "items": { + "$ref": "#/definitions/v3MessagePayloadDecoderExample" + } + } + } + }, + "v3MessagePayloadDecoderExample": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "input": { + "$ref": "#/definitions/v3EncodedMessagePayload" + }, + "output": { + "$ref": "#/definitions/v3DecodedMessagePayload" + } + } + }, + "v3MessagePayloadEncoder": { + "type": "object", + "properties": { + "formatter": { + "$ref": "#/definitions/v3PayloadFormatter", + "description": "Payload formatter type." + }, + "formatter_parameter": { + "type": "string", + "description": "Parameter for the formatter, must be set together." + }, + "codec_id": { + "type": "string" + }, + "examples": { + "type": "array", + "items": { + "$ref": "#/definitions/v3MessagePayloadEncoderExample" + } + } + } + }, + "v3MessagePayloadEncoderExample": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "input": { + "$ref": "#/definitions/v3DecodedMessagePayload" + }, + "output": { + "$ref": "#/definitions/v3EncodedMessagePayload" + } + } + }, "v3MessagePayloadFormatters": { "type": "object", "properties": { @@ -11746,7 +23189,7 @@ }, "up_formatter_parameter": { "type": "string", - "description": "Parameter for the up_formatter, must be set together." + "description": "Parameter for the up_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration." }, "down_formatter": { "$ref": "#/definitions/v3PayloadFormatter", @@ -11754,31 +23197,129 @@ }, "down_formatter_parameter": { "type": "string", - "description": "Parameter for the down_formatter, must be set together." + "description": "Parameter for the down_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration." + } + } + }, + "v3Minor": { + "type": "string", + "enum": [ + "MINOR_RFU_0", + "MINOR_1", + "MINOR_RFU_2", + "MINOR_RFU_3", + "MINOR_RFU_4", + "MINOR_RFU_5", + "MINOR_RFU_6", + "MINOR_RFU_7", + "MINOR_RFU_8", + "MINOR_RFU_9", + "MINOR_RFU_10", + "MINOR_RFU_11", + "MINOR_RFU_12", + "MINOR_RFU_13", + "MINOR_RFU_14", + "MINOR_RFU_15" + ], + "default": "MINOR_RFU_0" + }, + "v3NetworkIdentifiers": { + "type": "object", + "properties": { + "net_id": { + "type": "string", + "format": "string", + "example": "000013", + "description": "LoRa Alliance NetID." + }, + "tenant_id": { + "type": "string", + "description": "Optional tenant identifier for multi-tenant deployments." + }, + "cluster_id": { + "type": "string", + "description": "Cluster identifier of the Network Server." + }, + "cluster_address": { + "type": "string", + "description": "Cluster address of the Network Server." + }, + "tenant_address": { + "type": "string", + "description": "Optional tenant address for multi-tenant deployments." + } + }, + "description": "Identifies a Network Server." + }, + "v3Notification": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The immutable ID of the notification. Generated by the server." + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The time when the notification was triggered." + }, + "entity_ids": { + "$ref": "#/definitions/v3EntityIdentifiers", + "description": "The entity this notification is about." + }, + "notification_type": { + "type": "string", + "description": "The type of this notification." + }, + "data": { + "$ref": "#/definitions/protobufAny", + "description": "The data related to the notification." + }, + "sender_ids": { + "$ref": "#/definitions/v3UserIdentifiers", + "description": "If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification." + }, + "receivers": { + "type": "array", + "items": { + "$ref": "#/definitions/v3NotificationReceiver" + }, + "description": "Relation of the notification receiver to the entity." + }, + "email": { + "type": "boolean", + "description": "Whether an email was sent for the notification." + }, + "status": { + "$ref": "#/definitions/v3NotificationStatus", + "description": "The status of the notification." + }, + "status_updated_at": { + "type": "string", + "format": "date-time", + "description": "The time when the notification status was updated." } } }, - "v3Minor": { + "v3NotificationReceiver": { + "type": "string", + "enum": [ + "NOTIFICATION_RECEIVER_UNKNOWN", + "NOTIFICATION_RECEIVER_COLLABORATOR", + "NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT", + "NOTIFICATION_RECEIVER_TECHNICAL_CONTACT" + ], + "default": "NOTIFICATION_RECEIVER_UNKNOWN", + "description": " - NOTIFICATION_RECEIVER_COLLABORATOR: Notification is received by collaborators of the entity.\nIf the collaborator is an organization, the notification is received by organization members.\n - NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT: Notification is received by administrative contact of the entity.\nIf this is an organization, the notification is received by organization members.\n - NOTIFICATION_RECEIVER_TECHNICAL_CONTACT: Notification is received by technical contact of the entity.\nIf this is an organization, the notification is received by organization members." + }, + "v3NotificationStatus": { "type": "string", "enum": [ - "MINOR_RFU_0", - "MINOR_1", - "MINOR_RFU_2", - "MINOR_RFU_3", - "MINOR_RFU_4", - "MINOR_RFU_5", - "MINOR_RFU_6", - "MINOR_RFU_7", - "MINOR_RFU_8", - "MINOR_RFU_9", - "MINOR_RFU_10", - "MINOR_RFU_11", - "MINOR_RFU_12", - "MINOR_RFU_13", - "MINOR_RFU_14", - "MINOR_RFU_15" + "NOTIFICATION_STATUS_UNSEEN", + "NOTIFICATION_STATUS_SEEN", + "NOTIFICATION_STATUS_ARCHIVED" ], - "default": "MINOR_RFU_0" + "default": "NOTIFICATION_STATUS_UNSEEN" }, "v3NwkSKeysResponse": { "type": "object", @@ -11885,21 +23426,31 @@ "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3OrganizationIdentifiers" + "$ref": "#/definitions/v3OrganizationIdentifiers", + "description": "The identifiers of the organization. These are public and can be seen by any authenticated user in the network." }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the organization was created. This information is public and can be seen by any authenticated user in the network." }, "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the organization was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the organization was deleted. This information is public and can be seen by any authenticated user in the network." }, "name": { - "type": "string" + "type": "string", + "description": "The name of the organization. This information is public and can be seen by any authenticated user in the network." }, "description": { - "type": "string" + "type": "string", + "description": "A description for the organization." }, "attributes": { "type": "object", @@ -11913,7 +23464,13 @@ "items": { "$ref": "#/definitions/v3ContactInfo" }, - "description": "Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization." + "description": "Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" } } }, @@ -11954,76 +23511,394 @@ "enum": [ "PHY_UNKNOWN", "PHY_V1_0", + "TS001_V1_0", "PHY_V1_0_1", + "TS001_V1_0_1", "PHY_V1_0_2_REV_A", + "RP001_V1_0_2", "PHY_V1_0_2_REV_B", + "RP001_V1_0_2_REV_B", "PHY_V1_1_REV_A", + "RP001_V1_1_REV_A", "PHY_V1_1_REV_B", - "PHY_V1_0_3_REV_A" + "RP001_V1_1_REV_B", + "PHY_V1_0_3_REV_A", + "RP001_V1_0_3_REV_A", + "RP002_V1_0_0", + "RP002_V1_0_1", + "RP002_V1_0_2", + "RP002_V1_0_3" ], "default": "PHY_UNKNOWN" }, - "v3PacketBrokerMetadata": { + "v3PacketBrokerDefaultGatewayVisibility": { "type": "object", "properties": { - "message_id": { + "updated_at": { "type": "string", - "description": "Message identifier generated by Packet Broker Router." + "format": "date-time", + "description": "Timestamp when the policy got last updated." }, - "forwarder_net_id": { + "visibility": { + "$ref": "#/definitions/v3PacketBrokerGatewayVisibility" + } + } + }, + "v3PacketBrokerDefaultRoutingPolicy": { + "type": "object", + "properties": { + "updated_at": { "type": "string", - "format": "byte", - "description": "LoRa Alliance NetID of the Packet Broker Forwarder Member." + "format": "date-time", + "description": "Timestamp when the policy got last updated." }, - "forwarder_tenant_id": { + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + }, + "v3PacketBrokerDevAddrBlock": { + "type": "object", + "properties": { + "dev_addr_prefix": { + "$ref": "#/definitions/v3DevAddrPrefix" + }, + "home_network_cluster_id": { + "type": "string" + } + } + }, + "v3PacketBrokerGateway": { + "type": "object", + "properties": { + "ids": { + "$ref": "#/definitions/v3PacketBrokerGatewayGatewayIdentifiers" + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "This field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "technical_contact": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + }, + "antennas": { + "type": "array", + "items": { + "$ref": "#/definitions/v3GatewayAntenna" + } + }, + "status_public": { + "type": "boolean" + }, + "location_public": { + "type": "boolean" + }, + "frequency_plan_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "update_location_from_status": { + "type": "boolean" + }, + "online": { + "type": "boolean" + }, + "rx_rate": { + "type": "number", + "format": "float", + "description": "Received packets rate (number of packets per hour).\nThis field gets updated when a value is set." + }, + "tx_rate": { + "type": "number", + "format": "float", + "description": "Transmitted packets rate (number of packets per hour).\nThis field gets updated when a value is set." + } + }, + "description": "Gateway respresentation for Packet Broker.\nThis is a subset and superset of the Gateway message using the same data types and field tags to achieve initial wire compatibility.\nThere is no (longer) wire compatibility needed; new fields may use any tag." + }, + "v3PacketBrokerGatewayGatewayIdentifiers": { + "type": "object", + "properties": { + "gateway_id": { + "type": "string" + }, + "eui": { "type": "string", - "description": "Tenant ID managed by the Packet Broker Forwarder Member." + "format": "string", + "example": "70B3D57ED000ABCD" + } + } + }, + "v3PacketBrokerGatewayVisibility": { + "type": "object", + "properties": { + "location": { + "type": "boolean", + "description": "Show location." + }, + "antenna_placement": { + "type": "boolean", + "description": "Show antenna placement (indoor/outdoor)." + }, + "antenna_count": { + "type": "boolean", + "description": "Show antenna count." + }, + "fine_timestamps": { + "type": "boolean", + "description": "Show whether the gateway produces fine timestamps." + }, + "contact_info": { + "type": "boolean", + "description": "Show contact information." + }, + "status": { + "type": "boolean", + "description": "Show status (online/offline)." + }, + "frequency_plan": { + "type": "boolean", + "description": "Show frequency plan." + }, + "packet_rates": { + "type": "boolean", + "description": "Show receive and transmission packet rates." + } + } + }, + "v3PacketBrokerInfo": { + "type": "object", + "properties": { + "registration": { + "$ref": "#/definitions/v3PacketBrokerNetwork", + "description": "The current registration, unset if there isn't a registration." + }, + "forwarder_enabled": { + "type": "boolean", + "description": "Whether the server is configured as Forwarder (with gateways)." + }, + "home_network_enabled": { + "type": "boolean", + "description": "Whether the server is configured as Home Network (with end devices)." + }, + "register_enabled": { + "type": "boolean", + "description": "Whether the registration can be changed." + } + } + }, + "v3PacketBrokerNetwork": { + "type": "object", + "properties": { + "id": { + "$ref": "#/definitions/v3PacketBrokerNetworkIdentifier", + "description": "Packet Broker network identifier." + }, + "name": { + "type": "string", + "description": "Name of the network." + }, + "dev_addr_blocks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3PacketBrokerDevAddrBlock" + }, + "description": "DevAddr blocks that are assigned to this registration." + }, + "contact_info": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ContactInfo" + }, + "description": "Contact information.\nThis field is deprecated. Use administrative_contact and technical_contact instead." + }, + "administrative_contact": { + "$ref": "#/definitions/v3ContactInfo" + }, + "technical_contact": { + "$ref": "#/definitions/v3ContactInfo" + }, + "listed": { + "type": "boolean", + "description": "Whether the network is listed so it can be viewed by other networks." + } + } + }, + "v3PacketBrokerNetworkIdentifier": { + "type": "object", + "properties": { + "net_id": { + "type": "integer", + "format": "int64", + "description": "LoRa Alliance NetID." + }, + "tenant_id": { + "type": "string", + "description": "Tenant identifier if the registration leases DevAddr blocks from a NetID." + } + } + }, + "v3PacketBrokerNetworks": { + "type": "object", + "properties": { + "networks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3PacketBrokerNetwork" + } + } + } + }, + "v3PacketBrokerRegisterRequest": { + "type": "object", + "properties": { + "listed": { + "type": "boolean", + "description": "Whether the network should be listed in Packet Broker.\nIf unset, the value is taken from the registration settings." + } + } + }, + "v3PacketBrokerRouteHop": { + "type": "object", + "properties": { + "received_at": { + "type": "string", + "format": "date-time", + "description": "Time when the service received the message." + }, + "sender_name": { + "type": "string", + "description": "Sender of the message, typically the authorized client identifier." + }, + "sender_address": { + "type": "string", + "description": "Sender IP address or host name." + }, + "receiver_name": { + "type": "string", + "description": "Receiver of the message." }, + "receiver_agent": { + "type": "string", + "description": "Receiver agent." + } + } + }, + "v3PacketBrokerRoutingPolicies": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicy" + } + } + } + }, + "v3PacketBrokerRoutingPolicy": { + "type": "object", + "properties": { "forwarder_id": { + "$ref": "#/definitions/v3PacketBrokerNetworkIdentifier", + "description": "Packet Broker identifier of the Forwarder." + }, + "home_network_id": { + "$ref": "#/definitions/v3PacketBrokerNetworkIdentifier", + "description": "Packet Broker identifier of the Home Network." + }, + "updated_at": { "type": "string", - "description": "Forwarder identifier issued by the Packet Broker Forwarder Member." + "format": "date-time", + "description": "Timestamp when the policy got last updated." + }, + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." + }, + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." + } + } + }, + "v3PacketBrokerRoutingPolicyDownlink": { + "type": "object", + "properties": { + "join_accept": { + "type": "boolean", + "description": "Allow join-accept messages." + }, + "mac_data": { + "type": "boolean", + "description": "Allow downlink messages with FPort of 0." + }, + "application_data": { + "type": "boolean", + "description": "Allow downlink messages with FPort between 1 and 255." + } + } + }, + "v3PacketBrokerRoutingPolicyUplink": { + "type": "object", + "properties": { + "join_request": { + "type": "boolean", + "description": "Forward join-request messages." + }, + "mac_data": { + "type": "boolean", + "description": "Forward uplink messages with FPort of 0." }, - "home_network_net_id": { - "type": "string", - "format": "byte", - "description": "LoRa Alliance NetID of the Packet Broker Home Network Member." + "application_data": { + "type": "boolean", + "description": "Forward uplink messages with FPort between 1 and 255." }, - "home_network_tenant_id": { - "type": "string", - "description": "Tenant ID managed by the Packet Broker Home Network Member.\nThis value is empty if it cannot be determined by the Packet Broker Router." + "signal_quality": { + "type": "boolean", + "description": "Forward RSSI and SNR." }, - "hops": { - "type": "array", - "items": { - "$ref": "#/definitions/v3PacketBrokerRouteHop" - }, - "description": "Hops that the message passed. Each Packet Broker Router service appends an entry." + "localization": { + "type": "boolean", + "description": "Forward gateway location, RSSI, SNR and fine timestamp." } } }, - "v3PacketBrokerRouteHop": { + "v3ParseEndDeviceQRCodeRequest": { "type": "object", "properties": { - "received_at": { - "type": "string", - "format": "date-time", - "description": "Time when the service received the message." - }, - "sender_name": { + "format_id": { "type": "string", - "description": "Sender of the message, typically the authorized client identifier." + "description": "QR code format identifier.\nEnumerate available formats with the rpc `ListFormats`.\nIf this field is not specified, the server will attempt to parse the data with each known format." }, - "sender_address": { + "qr_code": { "type": "string", - "description": "Sender IP address or host name." - }, - "receiver_name": { + "format": "byte", + "description": "Raw QR code contents." + } + } + }, + "v3ParseEndDeviceQRCodeResponse": { + "type": "object", + "properties": { + "format_id": { "type": "string", - "description": "Receiver of the message." + "description": "Identifier of the format used to successfully parse the QR code data." }, - "receiver_agent": { - "type": "string", - "description": "Receiver agent." + "end_device_template": { + "$ref": "#/definitions/v3EndDeviceTemplate" } } }, @@ -12087,35 +23962,6 @@ "default": "POWER_UNKNOWN", "description": "Power state of the device." }, - "v3ProvisionEndDevicesRequest": { - "type": "object", - "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" - }, - "provisioner_id": { - "type": "string", - "description": "ID of the provisioner service as configured in the Join Server." - }, - "provisioning_data": { - "type": "string", - "format": "byte", - "description": "Vendor-specific provisioning data." - }, - "list": { - "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersList", - "description": "List of device identifiers that will be provisioned.\nThe device identifiers must contain device_id and dev_eui.\nIf set, the application_ids must equal the provision request's application_ids.\nThe number of entries in data must match the number of given identifiers." - }, - "range": { - "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersRange", - "description": "Provision devices in a range.\nThe device_id will be generated by the provisioner from the vendor-specific data.\nThe dev_eui will be issued from the given start_dev_eui." - }, - "from_data": { - "$ref": "#/definitions/ProvisionEndDevicesRequestIdentifiersFromData", - "description": "Provision devices with identifiers from the given data.\nThe device_id and dev_eui will be generated by the provisioner from the vendor-specific data." - } - } - }, "v3QRCodeFormat": { "type": "object", "properties": { @@ -12126,7 +23972,7 @@ "type": "string" }, "field_mask": { - "$ref": "#/definitions/protobufFieldMask", + "type": "string", "description": "The entity fields required to generate the QR code." } } @@ -12138,7 +23984,8 @@ "type": "object", "additionalProperties": { "$ref": "#/definitions/v3QRCodeFormat" - } + }, + "description": "Available formats. The map key is the format identifier." } } }, @@ -12182,19 +24029,22 @@ "type": "object", "properties": { "rejoin_type": { - "$ref": "#/definitions/v3RejoinType" + "$ref": "#/definitions/v3RejoinRequestType" }, "net_id": { "type": "string", - "format": "byte" + "format": "string", + "example": "000013" }, "join_eui": { "type": "string", - "format": "byte" + "format": "string", + "example": "70B3D57ED000ABCD" }, "dev_eui": { "type": "string", - "format": "byte" + "format": "string", + "example": "70B3D57ED000ABCD" }, "rejoin_cnt": { "type": "integer", @@ -12202,6 +24052,15 @@ } } }, + "v3RejoinRequestType": { + "type": "string", + "enum": [ + "CONTEXT", + "SESSION", + "KEYS" + ], + "default": "CONTEXT" + }, "v3RejoinTimeExponent": { "type": "string", "enum": [ @@ -12224,15 +24083,6 @@ ], "default": "REJOIN_TIME_0" }, - "v3RejoinType": { - "type": "string", - "enum": [ - "CONTEXT", - "SESSION", - "KEYS" - ], - "default": "CONTEXT" - }, "v3Right": { "type": "string", "enum": [ @@ -12250,6 +24100,7 @@ "RIGHT_USER_CLIENTS_CREATE", "RIGHT_USER_ORGANIZATIONS_LIST", "RIGHT_USER_ORGANIZATIONS_CREATE", + "RIGHT_USER_NOTIFICATIONS_READ", "RIGHT_USER_ALL", "RIGHT_APPLICATION_INFO", "RIGHT_APPLICATION_SETTINGS_BASIC", @@ -12267,6 +24118,10 @@ "RIGHT_APPLICATION_LINK", "RIGHT_APPLICATION_ALL", "RIGHT_CLIENT_ALL", + "RIGHT_CLIENT_INFO", + "RIGHT_CLIENT_SETTINGS_BASIC", + "RIGHT_CLIENT_SETTINGS_COLLABORATORS", + "RIGHT_CLIENT_DELETE", "RIGHT_GATEWAY_INFO", "RIGHT_GATEWAY_SETTINGS_BASIC", "RIGHT_GATEWAY_SETTINGS_API_KEYS", @@ -12277,6 +24132,8 @@ "RIGHT_GATEWAY_LINK", "RIGHT_GATEWAY_STATUS_READ", "RIGHT_GATEWAY_LOCATION_READ", + "RIGHT_GATEWAY_WRITE_SECRETS", + "RIGHT_GATEWAY_READ_SECRETS", "RIGHT_GATEWAY_ALL", "RIGHT_ORGANIZATION_INFO", "RIGHT_ORGANIZATION_SETTINGS_BASIC", @@ -12295,7 +24152,7 @@ "RIGHT_ALL" ], "default": "right_invalid", - "description": "Right is the enum that defines all the different rights to do something in the network.\n\n - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights." + "description": "Right is the enum that defines all the different rights to do something in the network.\n\n - RIGHT_USER_INFO: The right to view user information.\n - RIGHT_USER_SETTINGS_BASIC: The right to edit basic user settings.\n - RIGHT_USER_SETTINGS_API_KEYS: The right to view and edit user API keys.\n - RIGHT_USER_DELETE: The right to delete user account.\n - RIGHT_USER_AUTHORIZED_CLIENTS: The right to view and edit authorized OAuth clients of the user.\n - RIGHT_USER_APPLICATIONS_LIST: The right to list applications the user is a collaborator of.\n - RIGHT_USER_APPLICATIONS_CREATE: The right to create an application under the user account.\n - RIGHT_USER_GATEWAYS_LIST: The right to list gateways the user is a collaborator of.\n - RIGHT_USER_GATEWAYS_CREATE: The right to create a gateway under the account of the user.\n - RIGHT_USER_CLIENTS_LIST: The right to list OAuth clients the user is a collaborator of.\n - RIGHT_USER_CLIENTS_CREATE: The right to create an OAuth client under the account of the user.\n - RIGHT_USER_ORGANIZATIONS_LIST: The right to list organizations the user is a member of.\n - RIGHT_USER_ORGANIZATIONS_CREATE: The right to create an organization under the user account.\n - RIGHT_USER_NOTIFICATIONS_READ: The right to read notifications sent to the user.\n - RIGHT_USER_ALL: The pseudo-right for all (current and future) user rights.\n - RIGHT_APPLICATION_INFO: The right to view application information.\n - RIGHT_APPLICATION_SETTINGS_BASIC: The right to edit basic application settings.\n - RIGHT_APPLICATION_SETTINGS_API_KEYS: The right to view and edit application API keys.\n - RIGHT_APPLICATION_SETTINGS_COLLABORATORS: The right to view and edit application collaborators.\n - RIGHT_APPLICATION_SETTINGS_PACKAGES: The right to view and edit application packages and associations.\n - RIGHT_APPLICATION_DELETE: The right to delete application.\n - RIGHT_APPLICATION_DEVICES_READ: The right to view devices in application.\n - RIGHT_APPLICATION_DEVICES_WRITE: The right to create devices in application.\n - RIGHT_APPLICATION_DEVICES_READ_KEYS: The right to view device keys in application.\nNote that keys may not be stored in a way that supports viewing them.\n - RIGHT_APPLICATION_DEVICES_WRITE_KEYS: The right to edit device keys in application.\n - RIGHT_APPLICATION_TRAFFIC_READ: The right to read application traffic (uplink and downlink).\n - RIGHT_APPLICATION_TRAFFIC_UP_WRITE: The right to write uplink application traffic.\n - RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE: The right to write downlink application traffic.\n - RIGHT_APPLICATION_LINK: The right to link as Application to a Network Server for traffic exchange,\ni.e. read uplink and write downlink (API keys only).\nThis right is typically only given to an Application Server.\nThis right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ,\nand RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE.\n - RIGHT_APPLICATION_ALL: The pseudo-right for all (current and future) application rights.\n - RIGHT_CLIENT_ALL: The pseudo-right for all (current and future) OAuth client rights.\n - RIGHT_CLIENT_INFO: The right to read client information.\n - RIGHT_CLIENT_SETTINGS_BASIC: The right to edit basic client settings.\n - RIGHT_CLIENT_SETTINGS_COLLABORATORS: The right to view and edit client collaborators.\n - RIGHT_CLIENT_DELETE: The right to delete a client.\n - RIGHT_GATEWAY_INFO: The right to view gateway information.\n - RIGHT_GATEWAY_SETTINGS_BASIC: The right to edit basic gateway settings.\n - RIGHT_GATEWAY_SETTINGS_API_KEYS: The right to view and edit gateway API keys.\n - RIGHT_GATEWAY_SETTINGS_COLLABORATORS: The right to view and edit gateway collaborators.\n - RIGHT_GATEWAY_DELETE: The right to delete gateway.\n - RIGHT_GATEWAY_TRAFFIC_READ: The right to read gateway traffic.\n - RIGHT_GATEWAY_TRAFFIC_DOWN_WRITE: The right to write downlink gateway traffic.\n - RIGHT_GATEWAY_LINK: The right to link as Gateway to a Gateway Server for traffic exchange,\ni.e. write uplink and read downlink (API keys only)\nThis right is typically only given to a gateway.\nThis right implies RIGHT_GATEWAY_INFO.\n - RIGHT_GATEWAY_STATUS_READ: The right to view gateway status.\n - RIGHT_GATEWAY_LOCATION_READ: The right to view view gateway location.\n - RIGHT_GATEWAY_WRITE_SECRETS: The right to store secrets associated with this gateway.\n - RIGHT_GATEWAY_READ_SECRETS: The right to retrieve secrets associated with this gateway.\n - RIGHT_GATEWAY_ALL: The pseudo-right for all (current and future) gateway rights.\n - RIGHT_ORGANIZATION_INFO: The right to view organization information.\n - RIGHT_ORGANIZATION_SETTINGS_BASIC: The right to edit basic organization settings.\n - RIGHT_ORGANIZATION_SETTINGS_API_KEYS: The right to view and edit organization API keys.\n - RIGHT_ORGANIZATION_SETTINGS_MEMBERS: The right to view and edit organization members.\n - RIGHT_ORGANIZATION_DELETE: The right to delete organization.\n - RIGHT_ORGANIZATION_APPLICATIONS_LIST: The right to list the applications the organization is a collaborator of.\n - RIGHT_ORGANIZATION_APPLICATIONS_CREATE: The right to create an application under the organization.\n - RIGHT_ORGANIZATION_GATEWAYS_LIST: The right to list the gateways the organization is a collaborator of.\n - RIGHT_ORGANIZATION_GATEWAYS_CREATE: The right to create a gateway under the organization.\n - RIGHT_ORGANIZATION_CLIENTS_LIST: The right to list the OAuth clients the organization is a collaborator of.\n - RIGHT_ORGANIZATION_CLIENTS_CREATE: The right to create an OAuth client under the organization.\n - RIGHT_ORGANIZATION_ADD_AS_COLLABORATOR: The right to add the organization as a collaborator on an existing entity.\n - RIGHT_ORGANIZATION_ALL: The pseudo-right for all (current and future) organization rights.\n - RIGHT_SEND_INVITES: The right to send invites to new users.\nNote that this is not prefixed with \"USER_\"; it is not a right on the user entity.\n - RIGHT_ALL: The pseudo-right for all (current and future) possible rights." }, "v3Rights": { "type": "object", @@ -12356,104 +24213,52 @@ } } }, - "v3RxMetadata": { + "v3ScheduleDownlinkResponse": { "type": "object", "properties": { - "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" - }, - "packet_broker": { - "$ref": "#/definitions/v3PacketBrokerMetadata" - }, - "antenna_index": { - "type": "integer", - "format": "int64" - }, - "time": { - "type": "string", - "format": "date-time" - }, - "timestamp": { - "type": "integer", - "format": "int64", - "description": "Gateway concentrator timestamp when the Rx finished (microseconds)." - }, - "fine_timestamp": { - "type": "string", - "format": "uint64", - "description": "Gateway's internal fine timestamp when the Rx finished (nanoseconds)." - }, - "encrypted_fine_timestamp": { - "type": "string", - "format": "byte", - "description": "Encrypted gateway's internal fine timestamp when the Rx finished (nanoseconds)." - }, - "encrypted_fine_timestamp_key_id": { - "type": "string" - }, - "rssi": { - "type": "number", - "format": "float", - "description": "Received signal strength indicator (dBm).\nThis value equals `channel_rssi`." - }, - "signal_rssi": { - "type": "number", - "format": "float", - "description": "Received signal strength indicator of the signal (dBm)." - }, - "channel_rssi": { - "type": "number", - "format": "float", - "description": "Received signal strength indicator of the channel (dBm)." - }, - "rssi_standard_deviation": { - "type": "number", - "format": "float", - "description": "Standard deviation of the RSSI during preamble." - }, - "snr": { - "type": "number", - "format": "float", - "description": "Signal-to-noise ratio (dB)." - }, - "frequency_offset": { + "delay": { "type": "string", - "format": "int64", - "description": "Frequency offset (Hz)." - }, - "location": { - "$ref": "#/definitions/lorawanv3Location", - "description": "Antenna location; injected by the Gateway Server." - }, - "downlink_path_constraint": { - "$ref": "#/definitions/v3DownlinkPathConstraint", - "description": "Gateway downlink path constraint; injected by the Gateway Server." + "description": "The amount of time between the message has been scheduled and it will be transmitted by the gateway." }, - "uplink_token": { - "type": "string", - "format": "byte", - "description": "Uplink token to be included in the Tx request in class A downlink; injected by gateway, Gateway Server or fNS." + "downlink_path": { + "$ref": "#/definitions/v3DownlinkPath", + "description": "Downlink path chosen by the Gateway Server." }, - "channel_index": { - "type": "integer", - "format": "int64", - "description": "Index of the gateway channel that received the message." + "rx1": { + "type": "boolean", + "description": "Whether RX1 has been chosen for the downlink message.\nBoth RX1 and RX2 can be used for transmitting the same message by the same gateway." }, - "advanced": { - "type": "object", - "title": "Advanced metadata fields\n- can be used for advanced information or experimental features that are not yet formally defined in the API\n- field names are written in snake_case" + "rx2": { + "type": "boolean", + "description": "Whether RX2 has been chosen for the downlink message.\nBoth RX1 and RX2 can be used for transmitting the same message by the same gateway." } - }, - "description": "Contains metadata for a received message. Each antenna that receives\na message corresponds to one RxMetadata." + } }, - "v3ScheduleDownlinkResponse": { + "v3SearchAccountsResponse": { "type": "object", "properties": { - "delay": { - "type": "string" + "account_ids": { + "type": "array", + "items": { + "$ref": "#/definitions/v3OrganizationOrUserIdentifiers" + } } } }, + "v3Secret": { + "type": "object", + "properties": { + "key_id": { + "type": "string", + "description": "ID of the Key used to encrypt the secret." + }, + "value": { + "type": "string", + "format": "byte" + } + }, + "description": "Secret contains a secret value. It also contains the ID of the Encryption key used to encrypt it." + }, "v3SendInvitationRequest": { "type": "object", "properties": { @@ -12467,7 +24272,8 @@ "properties": { "dev_addr": { "type": "string", - "format": "byte", + "format": "string", + "example": "2600ABCD", "description": "Device Address, issued by the Network Server or chosen by device manufacturer in case of testing range (beginning with 00-03).\nKnown by Network Server, Application Server and Join Server. Owned by Network Server." }, "keys": { @@ -12498,153 +24304,60 @@ "format": "date-time", "description": "Time when the session started. Network Server only." }, - "queued_application_downlinks": { - "type": "array", - "items": { - "$ref": "#/definitions/v3ApplicationDownlink" - }, - "description": "Queued Application downlink messages. Stored in Application Server and Network Server." - } - } - }, - "v3SessionKeys": { - "type": "object", - "properties": { - "session_key_id": { - "type": "string", - "format": "byte", - "description": "Join Server issued identifier for the session keys.\nThis ID can be used to request the keys from the Join Server in case the are lost." - }, - "f_nwk_s_int_key": { - "$ref": "#/definitions/v3KeyEnvelope", - "description": "The (encrypted) Forwarding Network Session Integrity Key (or Network Session Key in 1.0 compatibility mode).\nThis key is stored by the (forwarding) Network Server." - }, - "s_nwk_s_int_key": { - "$ref": "#/definitions/v3KeyEnvelope", - "description": "The (encrypted) Serving Network Session Integrity Key.\nThis key is stored by the (serving) Network Server." - }, - "nwk_s_enc_key": { - "$ref": "#/definitions/v3KeyEnvelope", - "description": "The (encrypted) Network Session Encryption Key.\nThis key is stored by the (serving) Network Server." - }, - "app_s_key": { - "$ref": "#/definitions/v3KeyEnvelope", - "description": "The (encrypted) Application Session Key.\nThis key is stored by the Application Server." - } - }, - "description": "Session keys for a LoRaWAN session.\nOnly the components for which the keys were meant, will have the key-encryption-key (KEK) to decrypt the individual keys." - }, - "v3SetApplicationCollaboratorRequest": { - "type": "object", - "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" - }, - "collaborator": { - "$ref": "#/definitions/v3Collaborator" - } - } - }, - "v3SetApplicationLinkRequest": { - "type": "object", - "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" - }, - "link": { - "$ref": "#/definitions/v3ApplicationLink" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask" - } - } - }, - "v3SetApplicationPackageAssociationRequest": { - "type": "object", - "properties": { - "association": { - "$ref": "#/definitions/v3ApplicationPackageAssociation" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask" - } - } - }, - "v3SetApplicationPackageDefaultAssociationRequest": { - "type": "object", - "properties": { - "default": { - "$ref": "#/definitions/v3ApplicationPackageDefaultAssociation" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask" - } - } - }, - "v3SetApplicationPubSubRequest": { - "type": "object", - "properties": { - "pubsub": { - "$ref": "#/definitions/v3ApplicationPubSub" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask" + "queued_application_downlinks": { + "type": "array", + "items": { + "$ref": "#/definitions/v3ApplicationDownlink" + }, + "description": "Queued Application downlink messages. Stored in Application Server and Network Server." } } }, - "v3SetApplicationWebhookRequest": { + "v3SessionKeys": { "type": "object", "properties": { - "webhook": { - "$ref": "#/definitions/v3ApplicationWebhook" + "session_key_id": { + "type": "string", + "format": "byte", + "description": "Join Server issued identifier for the session keys.\nThis ID can be used to request the keys from the Join Server in case the are lost." }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask" - } - } - }, - "v3SetClientCollaboratorRequest": { - "type": "object", - "properties": { - "client_ids": { - "$ref": "#/definitions/v3ClientIdentifiers" + "f_nwk_s_int_key": { + "$ref": "#/definitions/v3KeyEnvelope", + "description": "The (encrypted) Forwarding Network Session Integrity Key (or Network Session Key in 1.0 compatibility mode).\nThis key is stored by the (forwarding) Network Server." }, - "collaborator": { - "$ref": "#/definitions/v3Collaborator" - } - } - }, - "v3SetEndDeviceRequest": { - "type": "object", - "properties": { - "end_device": { - "$ref": "#/definitions/v3EndDevice" + "s_nwk_s_int_key": { + "$ref": "#/definitions/v3KeyEnvelope", + "description": "The (encrypted) Serving Network Session Integrity Key.\nThis key is stored by the (serving) Network Server." }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." + "nwk_s_enc_key": { + "$ref": "#/definitions/v3KeyEnvelope", + "description": "The (encrypted) Network Session Encryption Key.\nThis key is stored by the (serving) Network Server." + }, + "app_s_key": { + "$ref": "#/definitions/v3KeyEnvelope", + "description": "The (encrypted) Application Session Key.\nThis key is stored by the Application Server." } - } + }, + "description": "Session keys for a LoRaWAN session.\nOnly the components for which the keys were meant, will have the key-encryption-key (KEK) to decrypt the individual keys." }, - "v3SetGatewayCollaboratorRequest": { + "v3SetPacketBrokerDefaultGatewayVisibilityRequest": { "type": "object", "properties": { - "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" - }, - "collaborator": { - "$ref": "#/definitions/v3Collaborator" + "visibility": { + "$ref": "#/definitions/v3PacketBrokerGatewayVisibility" } } }, - "v3SetOrganizationCollaboratorRequest": { + "v3SetPacketBrokerDefaultRoutingPolicyRequest": { "type": "object", "properties": { - "organization_ids": { - "$ref": "#/definitions/v3OrganizationIdentifiers" + "uplink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyUplink", + "description": "Uplink policy." }, - "collaborator": { - "$ref": "#/definitions/v3Collaborator" + "downlink": { + "$ref": "#/definitions/v3PacketBrokerRoutingPolicyDownlink", + "description": "Downlink policy." } } }, @@ -12678,6 +24391,13 @@ "type": "string", "format": "date-time", "description": "If not empty, this will return historical events after the given time when the stream starts.\nIf used in combination with \"tail\", the limit that is reached first, is used.\nThe availability of historical events depends on server support and retention policy." + }, + "names": { + "type": "array", + "items": { + "type": "string" + }, + "description": "If provided, this will filter events, so that only events with the given names are returned.\nNames can be provided as either exact event names (e.g. 'gs.up.receive'),\nor as regular expressions (e.g. '/^gs\\..+/')." } } }, @@ -12688,10 +24408,15 @@ "type": "array", "items": { "type": "string" - } + }, + "description": "Correlation IDs for the downlink message.\nSet automatically by the UDP and LBS frontends.\nFor gRPC and the MQTT v3 frontends, the correlation IDs must match the ones of the downlink message the Tx acknowledgment message refers to." }, "result": { "$ref": "#/definitions/TxAcknowledgmentResult" + }, + "downlink_message": { + "$ref": "#/definitions/lorawanv3DownlinkMessage", + "description": "The acknowledged downlink message. Set by the Gateway Server." } } }, @@ -12712,18 +24437,18 @@ "$ref": "#/definitions/v3RxDelay", "description": "Rx1 delay (Rx2 delay is Rx1 delay + 1 second)." }, - "rx1_data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex", - "description": "LoRaWAN data rate index for Rx1." + "rx1_data_rate": { + "$ref": "#/definitions/v3DataRate", + "description": "LoRaWAN data rate for Rx1." }, "rx1_frequency": { "type": "string", "format": "uint64", "description": "Frequency (Hz) for Rx1." }, - "rx2_data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex", - "description": "LoRaWAN data rate index for Rx2." + "rx2_data_rate": { + "$ref": "#/definitions/v3DataRate", + "description": "LoRaWAN data rate for Rx2." }, "rx2_frequency": { "type": "string", @@ -12763,240 +24488,44 @@ ], "default": "LOWEST" }, - "v3TxSettings": { + "v3UpdatePacketBrokerGatewayResponse": { "type": "object", "properties": { - "data_rate": { - "$ref": "#/definitions/v3DataRate", - "description": "Data rate." - }, - "data_rate_index": { - "$ref": "#/definitions/v3DataRateIndex", - "description": "LoRaWAN data rate index." - }, - "coding_rate": { - "type": "string", - "description": "LoRa coding rate." - }, - "frequency": { - "type": "string", - "format": "uint64", - "description": "Frequency (Hz)." - }, - "enable_crc": { - "type": "boolean", - "format": "boolean", - "description": "Send a CRC in the packet; only on uplink; on downlink, CRC should not be enabled." - }, - "timestamp": { - "type": "integer", - "format": "int64", - "description": "Timestamp of the gateway concentrator when the uplink message was received, or when the downlink message should be transmitted (microseconds).\nOn downlink, set timestamp to 0 and time to null to use immediate scheduling." - }, - "time": { + "online_ttl": { "type": "string", - "format": "date-time", - "description": "Time of the gateway when the uplink message was received, or when the downlink message should be transmitted.\nFor downlink, this requires the gateway to have GPS time synchronization." - }, - "downlink": { - "$ref": "#/definitions/TxSettingsDownlink", - "description": "Transmission settings for downlink." - } - }, - "description": "TxSettings contains the settings for a transmission.\nThis message is used on both uplink and downlink.\nOn downlink, this is a scheduled transmission." - }, - "v3UpdateApplicationAPIKeyRequest": { - "type": "object", - "properties": { - "application_ids": { - "$ref": "#/definitions/v3ApplicationIdentifiers" - }, - "api_key": { - "$ref": "#/definitions/v3APIKey" - } - } - }, - "v3UpdateApplicationRequest": { - "type": "object", - "properties": { - "application": { - "$ref": "#/definitions/v3Application" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the application fields that should be updated." - } - } - }, - "v3UpdateClientRequest": { - "type": "object", - "properties": { - "client": { - "$ref": "#/definitions/v3Client" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the client fields that should be updated." - } - } - }, - "v3UpdateEndDeviceRequest": { - "type": "object", - "properties": { - "end_device": { - "$ref": "#/definitions/v3EndDevice" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the end device fields that should be updated.\nSee the API reference for which fields can be set on the different services." - } - } - }, - "v3UpdateGatewayAPIKeyRequest": { - "type": "object", - "properties": { - "gateway_ids": { - "$ref": "#/definitions/v3GatewayIdentifiers" - }, - "api_key": { - "$ref": "#/definitions/v3APIKey" - } - } - }, - "v3UpdateGatewayRequest": { - "type": "object", - "properties": { - "gateway": { - "$ref": "#/definitions/v3Gateway" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the gateway fields that should be updated." - } - } - }, - "v3UpdateOrganizationAPIKeyRequest": { - "type": "object", - "properties": { - "organization_ids": { - "$ref": "#/definitions/v3OrganizationIdentifiers" - }, - "api_key": { - "$ref": "#/definitions/v3APIKey" - } - } - }, - "v3UpdateOrganizationRequest": { - "type": "object", - "properties": { - "organization": { - "$ref": "#/definitions/v3Organization" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the organization fields that should be updated." - } - } - }, - "v3UpdateUserAPIKeyRequest": { - "type": "object", - "properties": { - "user_ids": { - "$ref": "#/definitions/v3UserIdentifiers" - }, - "api_key": { - "$ref": "#/definitions/v3APIKey" - } - } - }, - "v3UpdateUserPasswordRequest": { - "type": "object", - "properties": { - "user_ids": { - "$ref": "#/definitions/v3UserIdentifiers" - }, - "new": { - "type": "string" - }, - "old": { - "type": "string" - }, - "revoke_all_access": { - "type": "boolean", - "format": "boolean", - "description": "Revoke active sessions and access tokens of user if true. To be used if credentials are suspected to be compromised." - } - } - }, - "v3UpdateUserRequest": { - "type": "object", - "properties": { - "user": { - "$ref": "#/definitions/v3User" - }, - "field_mask": { - "$ref": "#/definitions/protobufFieldMask", - "description": "The names of the user fields that should be updated." + "description": "Time to live of the online status." } } }, - "v3UplinkMessage": { - "type": "object", - "properties": { - "raw_payload": { - "type": "string", - "format": "byte" - }, - "payload": { - "$ref": "#/definitions/lorawanv3Message" - }, - "settings": { - "$ref": "#/definitions/v3TxSettings" - }, - "rx_metadata": { - "type": "array", - "items": { - "$ref": "#/definitions/v3RxMetadata" - } - }, - "received_at": { - "type": "string", - "format": "date-time", - "description": "Server time when a component received the message.\nThe Gateway Server and Network Server set this value to their local server time of reception." - }, - "correlation_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "device_channel_index": { - "type": "integer", - "format": "int64", - "description": "Index of the device channel that received the message.\nSet by Network Server." - } - }, - "title": "Uplink message from the end device to the network" - }, "v3User": { "type": "object", "properties": { "ids": { - "$ref": "#/definitions/v3UserIdentifiers" + "$ref": "#/definitions/v3UserIdentifiers", + "description": "The identifiers of the user. These are public and can be seen by any authenticated user in the network." }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the user was created. This information is public and can be seen by any authenticated user in the network." }, "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "description": "When the user was last updated. This information is public and can be seen by any authenticated user in the network." + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "description": "When the user was deleted. This information is public and can be seen by any authenticated user in the network." }, "name": { - "type": "string" + "type": "string", + "description": "The name of the user. This information is public and can be seen by any authenticated user in the network." }, "description": { - "type": "string" + "type": "string", + "description": "A description for the user. This information is public and can be seen by any authenticated user in the network." }, "attributes": { "type": "object", @@ -13010,7 +24539,7 @@ "items": { "$ref": "#/definitions/v3ContactInfo" }, - "description": "Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user." + "description": "Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user.\nThis field is deprecated." }, "primary_email_address": { "type": "string", @@ -13030,17 +24559,19 @@ "format": "date-time" }, "require_password_update": { - "type": "boolean", - "format": "boolean" + "type": "boolean" }, "state": { "$ref": "#/definitions/v3State", - "description": "The reviewing state of the user.\nThis field can only be modified by admins." + "description": "The reviewing state of the user.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by admins." + }, + "state_description": { + "type": "string", + "description": "A description for the state field.\nThis field can only be modified by admins, and should typically only be updated\nwhen also updating `state`." }, "admin": { "type": "boolean", - "format": "boolean", - "description": "This user is an admin.\nThis field can only be modified by other admins." + "description": "This user is an admin.\nThis information is public and can be seen by any authenticated user in the network.\nThis field can only be modified by other admins." }, "temporary_password": { "type": "string", @@ -13055,7 +24586,8 @@ "format": "date-time" }, "profile_picture": { - "$ref": "#/definitions/v3Picture" + "$ref": "#/definitions/v3Picture", + "description": "A profile picture for the user.\nThis information is public and can be seen by any authenticated user in the network." } }, "description": "User is the message that defines a user on the network." @@ -13121,6 +24653,15 @@ } } } + }, + "v3ZeroableFrequencyValue": { + "type": "object", + "properties": { + "value": { + "type": "string", + "format": "uint64" + } + } } } } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/application.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/application.proto index 1a7373b52..0a6dd7912 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/application.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/application.proto @@ -16,49 +16,134 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; import "lorawan-stack/api/contact_info.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/rights.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // Application is the message that defines an Application in the network. message Application { - ApplicationIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + // The identifiers of the application. These are public and can be seen by any authenticated user in the network. + ApplicationIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + // When the application was created. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the application was last updated. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the application was deleted. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp deleted_at = 8 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // The name of the application. string name = 4 [(validate.rules).string.max_len = 50]; + // A description for the application. string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this application. Typically used for organizing applications or for storing integration-specific data. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Contact information for this application. Typically used to indicate who to contact with technical/security questions about the application. - repeated ContactInfo contact_info = 7; + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; + + OrganizationOrUserIdentifiers administrative_contact = 10; + OrganizationOrUserIdentifiers technical_contact = 11; + + // The address of the Network Server where this application is supposed to be registered. + // If set, this fields indicates where end devices for this application should be registered. + // + // Stored in Entity Registry. + // The typical format of the address is "host:port". If the port is omitted, + // the normal port inference (with DNS lookup, otherwise defaults) is used. + // The connection shall be established with transport layer security (TLS). + // Custom certificate authorities may be configured out-of-band. + string network_server_address = 12 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + + // The address of the Application Server where this application is supposed to be registered. + // If set, this fields indicates where end devices for this application should be registered. + // + // Stored in Entity Registry. + // The typical format of the address is "host:port". If the port is omitted, + // the normal port inference (with DNS lookup, otherwise defaults) is used. + // The connection shall be established with transport layer security (TLS). + // Custom certificate authorities may be configured out-of-band. + string application_server_address = 13 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + + // The address of the Join Server where this application is supposed to be registered. + // If set, this fields indicates where end devices for this application should be registered. + // + // Stored in Entity Registry. + // The typical format of the address is "host:port". If the port is omitted, + // the normal port inference (with DNS lookup, otherwise defaults) is used. + // The connection shall be established with transport layer security (TLS). + // Custom certificate authorities may be configured out-of-band. + string join_server_address = 14 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + + uint32 dev_eui_counter = 9 [(thethings.flags.field) = { select: true, set: false }]; + + reserved 15; reserved "end_device_limit"; + + // next: 16 } message Applications { repeated Application applications = 1; } +message IssueDevEUIResponse { + bytes dev_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; +} + message GetApplicationRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; // The names of the application fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationsRequest { + option (thethings.flags.message) = { select: false, set: true }; // By default we list all applications the caller has rights on. // Set the user or the organization (not both) to instead list the applications // where the user or organization is collaborator on. - OrganizationOrUserIdentifiers collaborator = 1; + OrganizationOrUserIdentifiers collaborator = 1 [ + (thethings.flags.field) = { hidden: true } + ]; // The names of the application fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ @@ -68,22 +153,31 @@ message ListApplicationsRequest { uint32 limit = 4 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 5; + // Only return recently deleted applications. + bool deleted = 6; } message CreateApplicationRequest { - Application application = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Application application = 1 [(validate.rules).message.required = true]; // Collaborator to grant all rights on the newly created application. - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message UpdateApplicationRequest { - Application application = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Application application = 1 [(validate.rules).message.required = true]; // The names of the application fields that should be updated. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationAPIKeysRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: false, set: true }; + + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + // Order the results by this field path. + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "api_key_id", "-api_key_id", "name", "-name", "created_at", "-created_at", "expires_at", "-expires_at"] } + ]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. @@ -91,36 +185,50 @@ message ListApplicationAPIKeysRequest { } message GetApplicationAPIKeyRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; // Unique public identifier for the API key. - string key_id = 2 [(gogoproto.customname) = "KeyID"]; + string key_id = 2; } message CreateApplicationAPIKeyRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; string name = 2 [(validate.rules).string.max_len = 50]; - repeated Right rights = 3 [(validate.rules).repeated.items.enum.defined_only = true];; + repeated Right rights = 3 [ + (validate.rules).repeated = { + min_items: 1, + unique: true, + items: { enum: { defined_only: true } } + } + ]; + google.protobuf.Timestamp expires_at = 4 [(validate.rules).timestamp.gt_now = true]; } message UpdateApplicationAPIKeyRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - APIKey api_key = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + APIKey api_key = 2 [(validate.rules).message.required = true]; + // The names of the api key fields that should be updated. + google.protobuf.FieldMask field_mask = 3; } message ListApplicationCollaboratorsRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "id", "-id", "-rights", "rights"] } + ]; } message GetApplicationCollaboratorRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message SetApplicationCollaboratorRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - Collaborator collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + Collaborator collaborator = 2 [(validate.rules).message.required = true]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/application_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/application_services.proto index 2a11aaa03..a75aba03f 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/application_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/application_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/application.proto"; @@ -24,6 +25,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The ApplicationRegistry service, exposed by the Identity Server, is used to manage +// application registrations. service ApplicationRegistry { // Create a new application. This also sets the given organization or user as // first collaborator with all possible rights. @@ -79,8 +85,38 @@ service ApplicationRegistry { delete: "/applications/{application_id}" }; }; + + // Restore a recently deleted application. + // + // Deployment configuration may specify if, and for how long after deletion, + // entities can be restored. + rpc Restore(ApplicationIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/applications/{application_id}/restore" + }; + }; + + // Purge the application. This will release the application ID for reuse. + // All end devices must be deleted from the application before it can be deleted. + // The application owner is responsible for clearing data from any (external) integrations + // that may store and expose data by application ID + rpc Purge(ApplicationIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/applications/{application_id}/purge" + }; + }; + + // Request DevEUI from the configured address block for a device inside the application. + // The maximum number of DevEUI's issued per application can be configured. + rpc IssueDevEUI(ApplicationIdentifiers) returns (IssueDevEUIResponse) { + option (google.api.http) = { + post: "/applications/{application_id}/dev-eui" + }; + }; } +// The ApplicationAcces service, exposed by the Identity Server, is used to manage +// API keys and collaborators of applications. service ApplicationAccess { // List the rights the caller has on this application. rpc ListRights(ApplicationIdentifiers) returns (Rights) { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver.proto index 898dfe9ed..0154727bd 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver.proto @@ -16,7 +16,10 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/api/annotations.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; @@ -30,50 +33,91 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ApplicationLink { - // The address of the external Network Server where to link to. - // The typical format of the address is "host:port". If the port is omitted, - // the normal port inference (with DNS lookup, otherwise defaults) is used. - // Leave empty when linking to a cluster Network Server. - string network_server_address = 1 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; - string api_key = 2 [(gogoproto.customname) = "APIKey", (validate.rules).string.min_len = 1]; + option (thethings.flags.message) = { select: true, set: true }; + reserved 1; // Deprecated: network_server_address + reserved 2; // Deprecated: api_key + // Default message payload formatters to use when there are no formatters + // defined on the end device level. MessagePayloadFormatters default_formatters = 3; - // Enable TLS for linking to the external Network Server. - // For cluster-local Network Servers, the cluster's TLS setting is used. - bool tls = 4 [(gogoproto.customname) = "TLS"]; + reserved 4; // Deprecated: tls // Skip decryption of uplink payloads and encryption of downlink payloads. // Leave empty for the using the Application Server's default setting. google.protobuf.BoolValue skip_payload_crypto = 5; } message GetApplicationLinkRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message SetApplicationLinkRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ApplicationLink link = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 3 [(gogoproto.nullable) = false]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + ApplicationLink link = 2 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 3; } // Link stats as monitored by the Application Server. message ApplicationLinkStats { - google.protobuf.Timestamp linked_at = 1 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp linked_at = 1; string network_server_address = 2 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // Timestamp when the last upstream message has been received from a Network Server. // This can be a join-accept, uplink message or downlink message event. - google.protobuf.Timestamp last_up_received_at = 3 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_up_received_at = 3; // Number of upstream messages received. uint64 up_count = 4; // Timestamp when the last downlink message has been forwarded to a Network Server. - google.protobuf.Timestamp last_downlink_forwarded_at = 5 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_downlink_forwarded_at = 5; // Number of downlink messages forwarded. uint64 downlink_count = 6; } +// Application Server configuration. +message AsConfiguration { + message PubSub { + message Providers { + enum Status { + option (thethings.json.enum) = { marshal_as_string: true }; + + // No restrictions are in place. + ENABLED = 0; + // Warnings are being emitted that the provider will be deprecated in the future. + WARNING = 1; + // New integrations cannot be set up, and old ones do not start. + DISABLED = 2; + }; + + Status mqtt = 1; + Status nats = 2; + reserved 3; // Deprecated: aws_iot + } + + Providers providers = 1; + }; + PubSub pubsub = 1; + + message Webhooks { + int64 unhealthy_attempts_threshold = 1; + google.protobuf.Duration unhealthy_retry_interval = 2; + } + Webhooks webhooks = 2; +} + +message GetAsConfigurationRequest { + +} + +message GetAsConfigurationResponse { + AsConfiguration configuration = 1; +} + // The As service manages the Application Server. service As { + // Get a link configuration from the Application Server to Network Server. + // This only contains the configuration. Use GetLinkStats to view statistics and any link errors. rpc GetLink(GetApplicationLinkRequest) returns (ApplicationLink) { option (google.api.http) = { get: "/as/applications/{application_ids.application_id}/link" @@ -82,7 +126,8 @@ service As { // Set a link configuration from the Application Server a Network Server. // This call returns immediately after setting the link configuration; it does not wait for a link to establish. - // To get link statistics or errors, use the `GetLinkStats` call. + // To get link statistics or errors, use GetLinkStats. + // Note that there can only be one Application Server instance linked to a Network Server for a given application at a time. rpc SetLink(SetApplicationLinkRequest) returns (ApplicationLink) { option (google.api.http) = { put: "/as/applications/{application_ids.application_id}/link", @@ -90,6 +135,7 @@ service As { }; }; + // Delete the link between the Application Server and Network Server for the specified application. rpc DeleteLink(ApplicationIdentifiers) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/as/applications/{application_id}/link", @@ -104,39 +150,117 @@ service As { get: "/as/applications/{application_id}/link/stats" }; }; + + rpc GetConfiguration(GetAsConfigurationRequest) returns (GetAsConfigurationResponse) { + option (google.api.http) = { + get: "/as/configuration" + }; + }; +} + +// Container for multiple Application uplink messages. +message NsAsHandleUplinkRequest { + repeated ApplicationUp application_ups = 1 [(validate.rules).repeated.min_items = 1]; +} + +// The NsAs service connects a Network Server to an Application Server. +service NsAs { + // Handle Application uplink messages. + rpc HandleUplink(NsAsHandleUplinkRequest) returns (google.protobuf.Empty); +} + +message EncodeDownlinkRequest { + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + EndDeviceVersionIdentifiers version_ids = 2; + ApplicationDownlink downlink = 3 [(validate.rules).message.required = true]; + PayloadFormatter formatter = 4 [(validate.rules).enum.defined_only = true]; + string parameter = 5; +} + +message EncodeDownlinkResponse { + ApplicationDownlink downlink = 1; +} + +message DecodeUplinkRequest { + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + EndDeviceVersionIdentifiers version_ids = 2; + ApplicationUplink uplink = 3 [(validate.rules).message.required = true]; + PayloadFormatter formatter = 4 [(validate.rules).enum.defined_only = true]; + string parameter = 5; +} + +message DecodeUplinkResponse { + ApplicationUplink uplink = 1; +} + +message DecodeDownlinkRequest { + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + EndDeviceVersionIdentifiers version_ids = 2; + ApplicationDownlink downlink = 3 [(validate.rules).message.required = true]; + PayloadFormatter formatter = 4 [(validate.rules).enum.defined_only = true]; + string parameter = 5; +} + +message DecodeDownlinkResponse { + ApplicationDownlink downlink = 1; } // The AppAs service connects an application or integration to an Application Server. service AppAs { + // Subscribe to upstream messages. rpc Subscribe(ApplicationIdentifiers) returns (stream ApplicationUp); + // Push downlink messages to the end of the downlink queue. rpc DownlinkQueuePush(DownlinkQueueRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/push", body: "*" }; }; + // Replace the entire downlink queue with the specified messages. + // This can also be used to empty the queue by specifying no messages. rpc DownlinkQueueReplace(DownlinkQueueRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/replace", body: "*" }; }; + // List the items currently in the downlink queue. rpc DownlinkQueueList(EndDeviceIdentifiers) returns (ApplicationDownlinks) { option (google.api.http) = { get: "/as/applications/{application_ids.application_id}/devices/{device_id}/down" }; }; + // Get connection information to connect an MQTT client. rpc GetMQTTConnectionInfo(ApplicationIdentifiers) returns (MQTTConnectionInfo) { option (google.api.http) = { get: "/as/applications/{application_id}/mqtt-connection-info" }; }; + // Simulate an upstream message. This can be used to test integrations. rpc SimulateUplink(ApplicationUp) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/simulate", body: "*" }; }; + rpc EncodeDownlink(EncodeDownlinkRequest) returns (EncodeDownlinkResponse) { + option (google.api.http) = { + post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/encode", + body: "*" + }; + } + rpc DecodeUplink(DecodeUplinkRequest) returns (DecodeUplinkResponse) { + option (google.api.http) = { + post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/up/decode", + body: "*" + }; + } + rpc DecodeDownlink(DecodeDownlinkRequest) returns (DecodeDownlinkResponse) { + option (google.api.http) = { + post: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/down/decode", + body: "*" + }; + } } // The AsEndDeviceRegistry service allows clients to manage their end devices on the Application Server. diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_integrations_storage.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_integrations_storage.proto new file mode 100644 index 000000000..6484ddb73 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_integrations_storage.proto @@ -0,0 +1,136 @@ +// Copyright © 2020 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; +import "lorawan-stack/api/identifiers.proto"; +import "lorawan-stack/api/messages.proto"; + +package ttn.lorawan.v3; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +message GetStoredApplicationUpRequest { + // Query upstream messages from all end devices of an application. Cannot be used in conjunction with end_device_ids. + ApplicationIdentifiers application_ids = 1; + // Query upstream messages from a single end device. Cannot be used in conjunction with application_ids. + EndDeviceIdentifiers end_device_ids = 2; + + // Query upstream messages of a specific type. If not set, then all upstream messages are returned. + string type = 3 [(validate.rules).string = { in: [ + "", + "uplink_message", + "uplink_normalized", + "join_accept", + "downlink_ack", + "downlink_nack", + "downlink_sent", + "downlink_failed", + "downlink_queued", + "downlink_queue_invalidated", + "location_solved", + "service_data" + ] }]; + + // Limit number of results. + google.protobuf.UInt32Value limit = 4; + // Query upstream messages after this timestamp only. Cannot be used in conjunction with last. + google.protobuf.Timestamp after = 5; + // Query upstream messages before this timestamp only. Cannot be used in conjunction with last. + google.protobuf.Timestamp before = 6; + // Query uplinks on a specific FPort only. + google.protobuf.UInt32Value f_port = 7; + + // Order results. + string order = 8 [(validate.rules).string = { + in: [ "", "-received_at", "received_at" ] + }]; + + // The names of the upstream message fields that should be returned. See the API reference + // for allowed field names for each type of upstream message. + google.protobuf.FieldMask field_mask = 9; + + // Query upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before. + google.protobuf.Duration last = 10; +} + +message GetStoredApplicationUpCountRequest { + // Count upstream messages from all end devices of an application. Cannot be used in conjunction with end_device_ids. + ApplicationIdentifiers application_ids = 1; + // Count upstream messages from a single end device. Cannot be used in conjunction with application_ids. + EndDeviceIdentifiers end_device_ids = 2; + + // Count upstream messages of a specific type. If not set, then all upstream messages are returned. + string type = 3 [(validate.rules).string = { in: [ + "", + "uplink_message", + "join_accept", + "downlink_ack", + "downlink_nack", + "downlink_sent", + "downlink_failed", + "downlink_queued", + "downlink_queue_invalidated", + "location_solved", + "service_data" + ] }]; + + // Count upstream messages after this timestamp only. Cannot be used in conjunction with last. + google.protobuf.Timestamp after = 4; + // Count upstream messages before this timestamp only. Cannot be used in conjunction with last. + google.protobuf.Timestamp before = 5; + // Count uplinks on a specific FPort only. + google.protobuf.UInt32Value f_port = 6; + + // Count upstream messages that have arrived in the last minutes or hours. Cannot be used in conjunction with after and before. + google.protobuf.Duration last = 7; +} + + +message GetStoredApplicationUpCountResponse { + // Number of stored messages by end device ID. + map count = 1; +} + +// The ApplicationUpStorage service can be used to query stored application upstream messages. +service ApplicationUpStorage { + // Returns a stream of application messages that have been stored in the database. + rpc GetStoredApplicationUp(GetStoredApplicationUpRequest) returns(stream ApplicationUp) { + option (google.api.http) = { + get: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}" + additional_bindings { + get: "/as/applications/{application_ids.application_id}/packages/storage/{type}" + } + }; + } + // Returns how many application messages have been stored in the database for an application or end device. + rpc GetStoredApplicationUpCount(GetStoredApplicationUpCountRequest) returns(GetStoredApplicationUpCountResponse) { + option (google.api.http) = { + get: "/as/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}/packages/storage/{type}/count" + additional_bindings { + get: "/as/applications/{application_ids.application_id}/packages/storage/{type}/count" + } + }; + } +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_packages.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_packages.proto index 824deca59..b6522c505 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_packages.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_packages.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; @@ -27,6 +28,9 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ApplicationPackage { string name = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; uint32 default_f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255}]; @@ -37,14 +41,23 @@ message ApplicationPackages { } message ApplicationPackageAssociationIdentifiers { - EndDeviceIdentifiers end_device_ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: true, set: true }; + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; uint32 f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255}]; } message ApplicationPackageAssociation { - ApplicationPackageAssociationIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationPackageAssociationIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; string package_name = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; google.protobuf.Struct data = 5; } @@ -54,34 +67,43 @@ message ApplicationPackageAssociations { } message GetApplicationPackageAssociationRequest { - ApplicationPackageAssociationIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPackageAssociationIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationPackageAssociationRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; + EndDeviceIdentifiers ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. // Each page is ordered by the FPort. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; - google.protobuf.FieldMask field_mask = 4 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 4; } message SetApplicationPackageAssociationRequest { - ApplicationPackageAssociation association = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPackageAssociation association = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ApplicationPackageDefaultAssociationIdentifiers { - ApplicationIdentifiers application_ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; uint32 f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255}]; } message ApplicationPackageDefaultAssociation { - ApplicationPackageDefaultAssociationIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationPackageDefaultAssociationIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; string package_name = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; google.protobuf.Struct data = 5; } @@ -91,23 +113,23 @@ message ApplicationPackageDefaultAssociations { } message GetApplicationPackageDefaultAssociationRequest { - ApplicationPackageDefaultAssociationIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPackageDefaultAssociationIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationPackageDefaultAssociationRequest { - ApplicationIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; + ApplicationIdentifiers ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. // Each page is ordered by the FPort. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; - google.protobuf.FieldMask field_mask = 4 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 4; } message SetApplicationPackageDefaultAssociationRequest { - ApplicationPackageDefaultAssociation default = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPackageDefaultAssociation default = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } service ApplicationPackageRegistry { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_pubsub.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_pubsub.proto index 2b07367f3..b106088b2 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_pubsub.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_pubsub.proto @@ -14,9 +14,12 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/api/annotations.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; @@ -26,15 +29,27 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ApplicationPubSubIdentifiers { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string pub_sub_id = 2 [(gogoproto.customname) = "PubSubID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: false, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + string pub_sub_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } message ApplicationPubSub { - ApplicationPubSubIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationPubSubIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; // The format to use for the body. // Supported values depend on the Application Server configuration. @@ -42,44 +57,113 @@ message ApplicationPubSub { // The NATS provider settings. message NATSProvider { + option (thethings.flags.message) = { select: true, set: true }; // The server connection URL. - string server_url = 1 [(gogoproto.customname) = "ServerURL", (validate.rules).string.uri = true]; + string server_url = 1 [(validate.rules).string.uri = true]; } // The MQTT provider settings. message MQTTProvider { - string server_url = 1 [(gogoproto.customname) = "ServerURL", (validate.rules).string.uri = true]; - string client_id = 2 [(gogoproto.customname) = "ClientID", (validate.rules).string.max_len = 23]; + option (thethings.flags.message) = { select: true, set: true }; + string server_url = 1 [(validate.rules).string.uri = true]; + string client_id = 2 [(validate.rules).string.max_len = 23]; string username = 3 [(validate.rules).string.max_len = 100]; string password = 4 [(validate.rules).string.max_len = 100]; enum QoS { + option (thethings.json.enum) = { marshal_as_string: true }; + AT_MOST_ONCE = 0; AT_LEAST_ONCE = 1; EXACTLY_ONCE = 2; } - QoS subscribe_qos = 5 [(gogoproto.customname) = "SubscribeQoS"]; - QoS publish_qos = 6 [(gogoproto.customname) = "PublishQoS"]; + QoS subscribe_qos = 5; + QoS publish_qos = 6; - bool use_tls = 7 [(gogoproto.customname) = "UseTLS"]; + bool use_tls = 7; // The server Root CA certificate. PEM formatted. - bytes tls_ca = 8 [(gogoproto.customname) = "TLSCA"]; + bytes tls_ca = 8 [ + (validate.rules).bytes.max_len = 8192, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; // The client certificate. PEM formatted. - bytes tls_client_cert = 9 [(gogoproto.customname) = "TLSClientCert"]; + bytes tls_client_cert = 9 [ + (validate.rules).bytes.max_len = 8192, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; // The client private key. PEM formatted. - bytes tls_client_key = 10 [(gogoproto.customname) = "TLSClientKey"]; + bytes tls_client_key = 10 [ + (validate.rules).bytes.max_len = 8192, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; + + // HTTP headers to use on MQTT-over-Websocket connections. + map headers = 11; } + + message AWSIoTProvider { + option (thethings.flags.message) = { select: true, set: true }; + // The AWS region. + string region = 1 [(validate.rules).string = { in: ["af-south-1", "ap-east-1", "ap-northeast-1", "ap-northeast-2", "ap-south-1", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-north-1", "eu-south-1", "eu-west-1", "eu-west-2", "eu-west-3", "me-south-1", "sa-east-1", "us-east-1", "us-east-2", "us-west-1", "us-west-2"] }]; + + message AccessKey { + option (thethings.flags.message) = { select: true, set: true }; + string access_key_id = 1 [(validate.rules).string = {pattern: "^[\\w]*$", min_len: 16, max_len: 128}]; + string secret_access_key = 2 [(validate.rules).string.max_len = 40]; + string session_token = 3 [(validate.rules).string.max_len = 256]; + } + + // If set, the integration will use an AWS access key. + AccessKey access_key = 2; + + message AssumeRole { + option (thethings.flags.message) = { select: true, set: true }; + string arn = 1 [(validate.rules).string = {pattern: "^arn:aws:iam::[0-9]{12}:role\\/[A-Za-z0-9_+=,.@-]+$"}]; + string external_id = 2 [(validate.rules).string = {pattern: "^[\\w+=,.@:\\/-]*$", max_len: 1224}]; + google.protobuf.Duration session_duration = 3; + } + + // If set, the integration will assume the given role during operation. + AssumeRole assume_role = 3; + + // The endpoint address to connect to. If the endpoint address is left empty, + // the integration will try to discover it. + string endpoint_address = 4 [(validate.rules).string = {pattern: "^((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])|)$", max_len: 128}]; + + message DefaultIntegration { + option (thethings.flags.message) = { select: true, set: true }; + // The stack name that is associated with the CloudFormation deployment of The Things Stack Enterprise integration. + string stack_name = 1 [(validate.rules).string = {pattern: "^[A-Za-z][A-Za-z0-9\\-]*$", max_len: 128}]; + } + + oneof deployment { + // Enable the default integration. This overrides custom base topic and message topics of the pub/sub integration. + DefaultIntegration default = 5; + } + } + // The provider for the PubSub. oneof provider { option (validate.required) = true; - NATSProvider nats = 17 [(gogoproto.customname) = "NATS"]; - MQTTProvider mqtt = 25 [(gogoproto.customname) = "MQTT"]; + NATSProvider nats = 17; + MQTTProvider mqtt = 25; + AWSIoTProvider aws_iot = 101; }; // Base topic name to which the messages topic is appended. string base_topic = 6 [(validate.rules).string.max_len = 100]; message Message { + option (thethings.flags.message) = { select: true, set: true }; // The topic on which the Application Server publishes or receives the messages. string topic = 1 [(validate.rules).string.max_len = 100]; } @@ -90,14 +174,18 @@ message ApplicationPubSub { Message downlink_replace = 8; Message uplink_message = 9; + Message uplink_normalized = 20; Message join_accept = 10; Message downlink_ack = 11; Message downlink_nack = 12; Message downlink_sent = 13; Message downlink_failed = 14; Message downlink_queued = 15; + Message downlink_queue_invalidated = 19; Message location_solved = 16; Message service_data = 18; + + // next: 21 } message ApplicationPubSubs { @@ -110,18 +198,19 @@ message ApplicationPubSubFormats { } message GetApplicationPubSubRequest { - ApplicationPubSubIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPubSubIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationPubSubsRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + option (thethings.flags.message) = { select: false, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message SetApplicationPubSubRequest { - ApplicationPubSub pubsub = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationPubSub pubsub = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } service ApplicationPubSubRegistry { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_web.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_web.proto index b9d08b94e..9976f79a8 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_web.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/applicationserver_web.proto @@ -14,115 +14,175 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; +import "lorawan-stack/api/error.proto"; import "lorawan-stack/api/identifiers.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ApplicationWebhookIdentifiers { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string webhook_id = 2 [(gogoproto.customname) = "WebhookID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + string webhook_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } message ApplicationWebhookTemplateIdentifiers { - string template_id = 1 [(gogoproto.customname) = "TemplateID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: true, set: true }; + string template_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } // ApplicationWebhookTemplateField represents a custom field that needs to be filled by the user in order to use the template. // A field can be an API key, an username or password, or any custom platform specific field (such as region). // The fields are meant to be replaced inside the URLs and headers when the webhook is created. message ApplicationWebhookTemplateField { - string id = 1 [(gogoproto.customname) = "ID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + string id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; string name = 2 [(validate.rules).string.max_len = 20]; string description = 3 [(validate.rules).string.max_len = 100]; // Secret decides if the field should be shown in plain-text or should stay hidden. bool secret = 4; string default_value = 5 [(validate.rules).string.max_len = 100]; + bool optional = 6; } message ApplicationWebhookTemplate { - ApplicationWebhookTemplateIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationWebhookTemplateIdentifiers ids = 1 [(validate.rules).message.required = true]; string name = 2 [(validate.rules).string.max_len = 20]; string description = 3 [(validate.rules).string.max_len = 100]; - string logo_url = 4 [(gogoproto.customname) = "LogoURL", (validate.rules).string.uri = true]; - string info_url = 5 [(gogoproto.customname) = "InfoURL", (validate.rules).string.uri = true]; - string documentation_url = 6 [(gogoproto.customname) = "DocumentationURL", (validate.rules).string.uri = true]; + string logo_url = 4 [(validate.rules).string.uri = true]; + string info_url = 5 [(validate.rules).string.uri = true]; + string documentation_url = 6 [(validate.rules).string.uri = true]; // The base URL of the template. Can contain template fields, in RFC 6570 format. - string base_url = 7 [(gogoproto.customname) = "BaseURL", (validate.rules).string.uri = true]; + string base_url = 7 [(validate.rules).string.uri = true]; // The HTTP headers used by the template. Both the key and the value can contain template fields. - map headers = 8; + map headers = 8 [ + (validate.rules).map = { + max_pairs: 50, + keys: { string: { max_len: 64 } }, + values: { string: { max_len: 256 } } + } + ]; string format = 9 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 20}]; repeated ApplicationWebhookTemplateField fields = 10; // Control the creation of the downlink queue operations API key. - bool create_downlink_api_key = 19 [(gogoproto.customname) = "CreateDownlinkAPIKey"]; + bool create_downlink_api_key = 19; message Message { // Path to append to the base URL. Can contain template fields, in RFC 6570 format. - string path = 1; + string path = 1 [(validate.rules).string.max_len = 64]; } Message uplink_message = 11; + Message uplink_normalized = 23; Message join_accept = 12; Message downlink_ack = 13; Message downlink_nack = 14; Message downlink_sent = 15; Message downlink_failed = 16; Message downlink_queued = 17; + Message downlink_queue_invalidated = 21; Message location_solved = 18; Message service_data = 20; + + google.protobuf.FieldMask field_mask = 22; + + // next: 24 } message ApplicationWebhookTemplates { repeated ApplicationWebhookTemplate templates = 1; } +message ApplicationWebhookHealth { + option (thethings.flags.message) = { select: true, set: false }; + message WebhookHealthStatusHealthy { + option (thethings.flags.message) = { select: true, set: false }; + } + message WebhookHealthStatusUnhealthy { + option (thethings.flags.message) = { select: true, set: false }; + uint64 failed_attempts = 1; + google.protobuf.Timestamp last_failed_attempt_at = 2 [(validate.rules).timestamp.required = true]; + ErrorDetails last_failed_attempt_details = 3; + } + oneof status { + WebhookHealthStatusHealthy healthy = 1; + WebhookHealthStatusUnhealthy unhealthy = 2; + } +} + message ApplicationWebhook { - ApplicationWebhookIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + ApplicationWebhookIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; // Base URL to which the message's path is appended. - string base_url = 4 [(gogoproto.customname) = "BaseURL", (validate.rules).string.uri = true]; + string base_url = 4 [(validate.rules).string.uri = true]; // HTTP headers to use. - map headers = 5; + map headers = 5 [ + (validate.rules).map = { + max_pairs: 50, + keys: { string: { max_len: 64 } }, + values: { string: { max_len: 4096 } } + } + ]; // The format to use for the body. // Supported values depend on the Application Server configuration. string format = 6 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 20}]; // The ID of the template that was used to create the Webhook. - ApplicationWebhookTemplateIdentifiers template_ids = 15 [(gogoproto.embed) = true]; + ApplicationWebhookTemplateIdentifiers template_ids = 15; // The value of the fields used by the template. Maps field.id to the value. map template_fields = 16; // The API key to be used for downlink queue operations. // The field is provided for convenience reasons, and can contain API keys with additional rights (albeit this is discouraged). - string downlink_api_key = 17 [(gogoproto.customname) = "DownlinkAPIKey"]; + string downlink_api_key = 17 [(validate.rules).string.max_len = 128]; message Message { + option (thethings.flags.message) = { select: true, set: true }; // Path to append to the base URL. - string path = 1; + string path = 1 [(validate.rules).string.max_len = 64]; } Message uplink_message = 7; + Message uplink_normalized = 22; Message join_accept = 8; Message downlink_ack = 9; Message downlink_nack = 10; Message downlink_sent = 11; Message downlink_failed = 12; Message downlink_queued = 13; + Message downlink_queue_invalidated = 19; Message location_solved = 14; Message service_data = 18; + + ApplicationWebhookHealth health_status = 20; + + google.protobuf.FieldMask field_mask = 21; + + // next: 23 } message ApplicationWebhooks { @@ -135,27 +195,27 @@ message ApplicationWebhookFormats { } message GetApplicationWebhookRequest { - ApplicationWebhookIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationWebhookIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationWebhooksRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message SetApplicationWebhookRequest { - ApplicationWebhook webhook = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationWebhook webhook = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message GetApplicationWebhookTemplateRequest { - ApplicationWebhookTemplateIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + ApplicationWebhookTemplateIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; } message ListApplicationWebhookTemplatesRequest { - google.protobuf.FieldMask field_mask = 1 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 1; } service ApplicationWebhookRegistry { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/client.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/client.proto index 6f3867324..9c165c235 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/client.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/client.proto @@ -14,8 +14,10 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/timestamp.proto"; import "lorawan-stack/api/contact_info.proto"; @@ -27,9 +29,12 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // The OAuth2 flows an OAuth client can use to get an access token. enum GrantType { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "GRANT" }; // Grant type used to exchange an authorization code for an access token. GRANT_AUTHORIZATION_CODE = 0; @@ -41,45 +46,97 @@ enum GrantType { // An OAuth client on the network. message Client { - ClientIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + // The identifiers of the OAuth client. These are public and can be seen by any authenticated user in the network. + ClientIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + // When the OAuth client was created. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the OAuth client was last updated. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the OAuth client was deleted. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp deleted_at = 16 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // The name of the OAuth client. This information is public and can be seen by any authenticated user in the network. string name = 4 [(validate.rules).string.max_len = 50]; + // A description for the OAuth client. This information is public and can be seen by any authenticated user in the network. string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this client. Typically used for organizing clients or for storing integration-specific data. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Contact information for this client. Typically used to indicate who to contact with technical/security questions about the application. - repeated ContactInfo contact_info = 7; + // This information is public and can be seen by any authenticated user in the network. + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; + + OrganizationOrUserIdentifiers administrative_contact = 18; + OrganizationOrUserIdentifiers technical_contact = 19; // The client secret is only visible to collaborators of the client. - string secret = 8; + string secret = 8 [(validate.rules).string.max_len = 128]; // The allowed redirect URIs against which authorization requests are checked. // If the authorization request does not pass a redirect URI, the first one // from this list is taken. - repeated string redirect_uris = 9 [(gogoproto.customname) = "RedirectURIs"]; + // This information is public and can be seen by any authenticated user in the network. + repeated string redirect_uris = 9 [ + (validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 128 } } + } + ]; // The allowed logout redirect URIs against which client initiated logout // requests are checked. If the authorization request does not pass a redirect // URI, the first one from this list is taken. - repeated string logout_redirect_uris = 15 [(gogoproto.customname) = "LogoutRedirectURIs"]; + // This information is public and can be seen by any authenticated user in the network. + repeated string logout_redirect_uris = 15 [ + (validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 128 } } + } + ]; // The reviewing state of the client. + // This information is public and can be seen by any authenticated user in the network. // This field can only be modified by admins. + // If state_description is not updated when updating state, state_description is cleared. State state = 10 [(validate.rules).enum.defined_only = true]; + // A description for the state field. + // This field can only be modified by admins, and should typically only be updated + // when also updating `state`. + string state_description = 17 [(validate.rules).string.max_len = 128]; // If set, the authorization page will be skipped. + // This information is public and can be seen by any authenticated user in the network. // This field can only be modified by admins. bool skip_authorization = 11; // If set, the authorization page will show endorsement. + // This information is public and can be seen by any authenticated user in the network. // This field can only be modified by admins. bool endorsed = 12; // OAuth flows that can be used for the client to get a token. + // This information is public and can be seen by any authenticated user in the network. // After a client is created, this field can only be modified by admins. repeated GrantType grants = 13 [(validate.rules).repeated.items.enum.defined_only = true]; // Rights denotes what rights the client will have access to. + // This information is public and can be seen by any authenticated user in the network. // Users that previously authorized this client will have to re-authorize the // client after rights are added to this list. repeated Right rights = 14 [(validate.rules).repeated.items.enum.defined_only = true]; + + // next: 20 } message Clients { @@ -87,18 +144,21 @@ message Clients { } message GetClientRequest { - ClientIdentifiers client_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ClientIdentifiers client_ids = 1 [(validate.rules).message.required = true]; // The names of the client fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListClientsRequest { + option (thethings.flags.message) = { select: false, set: true }; // By default we list all OAuth clients the caller has rights on. // Set the user or the organization (not both) to instead list the OAuth clients // where the user or organization is collaborator on. - OrganizationOrUserIdentifiers collaborator = 1; + OrganizationOrUserIdentifiers collaborator = 1 [ + (thethings.flags.field) = { hidden: true } + ]; // The names of the client fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ @@ -108,34 +168,41 @@ message ListClientsRequest { uint32 limit = 4 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 5; + // Only return recently deleted clients. + bool deleted = 6; } message CreateClientRequest { - Client client = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Client client = 1 [(validate.rules).message.required = true]; // Collaborator to grant all rights on the newly created client. - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message UpdateClientRequest { - Client client = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Client client = 1 [(validate.rules).message.required = true]; // The names of the client fields that should be updated. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListClientCollaboratorsRequest { - ClientIdentifiers client_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ClientIdentifiers client_ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "id", "-id", "-rights", "rights"] } + ]; } message GetClientCollaboratorRequest { - ClientIdentifiers client_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ClientIdentifiers client_ids = 1 [(validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message SetClientCollaboratorRequest { - ClientIdentifiers client_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - Collaborator collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + ClientIdentifiers client_ids = 1 [(validate.rules).message.required = true]; + Collaborator collaborator = 2 [(validate.rules).message.required = true]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/client_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/client_services.proto index fe5b3d3a9..c10bb8f58 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/client_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/client_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/client.proto"; @@ -24,6 +25,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The ClientRegistry service, exposed by the Identity Server, is used to manage +// OAuth client registrations. service ClientRegistry { // Create a new OAuth client. This also sets the given organization or user as // first collaborator with all possible rights. @@ -50,7 +56,7 @@ service ClientRegistry { // List OAuth clients where the given user or organization is a direct collaborator. // If no user or organization is given, this returns the OAuth clients the caller // has access to. - // Similar to Get, this selects the fields sepcified in the field mask. + // Similar to Get, this selects the fields specified in the field mask. // More or less fields may be returned, depending on the rights of the caller. rpc List(ListClientsRequest) returns (Clients) { option (google.api.http) = { @@ -78,8 +84,27 @@ service ClientRegistry { delete: "/clients/{client_id}" }; }; + + // Restore a recently deleted client. + // + // Deployment configuration may specify if, and for how long after deletion, + // entities can be restored. + rpc Restore(ClientIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/clients/{client_id}/restore" + }; + }; + + // Purge the client. This will release the client ID for reuse. + rpc Purge(ClientIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/clients/{client_id}/purge" + }; + }; } +// The ClientAcces service, exposed by the Identity Server, is used to manage +// collaborators of OAuth clients. service ClientAccess { // List the rights the caller has on this application. rpc ListRights(ClientIdentifiers) returns (Rights) { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/cluster.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/cluster.proto index f87801e75..83e3710a6 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/cluster.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/cluster.proto @@ -21,12 +21,15 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // PeerInfo message PeerInfo { // Port on which the gRPC server is exposed. - uint32 grpc_port = 1 [(gogoproto.customname) = "GRPCPort"]; + uint32 grpc_port = 1; // Indicates whether the gRPC server uses TLS. - bool tls = 2 [(gogoproto.customname) = "TLS"]; + bool tls = 2; // Roles of the peer. repeated ClusterRole roles = 3; diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/configuration_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/configuration_services.proto index 1dd6fa541..654671eec 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/configuration_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/configuration_services.proto @@ -14,22 +14,31 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; import "google/api/annotations.proto"; +import "google/protobuf/wrappers.proto"; +import "lorawan-stack/api/lorawan.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ListFrequencyPlansRequest { + option (thethings.flags.message) = { select: false, set: true }; // Optional base frequency in MHz for hardware support (433, 470, 868 or 915) uint32 base_frequency = 1; } message FrequencyPlanDescription { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; // The ID of the frequency that the current frequency plan is based on. - string base_id = 2 [(gogoproto.customname) = "BaseID"]; + string base_id = 2; string name = 3; // Base frequency in MHz for hardware support (433, 470, 868 or 915) uint32 base_frequency = 4; @@ -39,10 +48,141 @@ message ListFrequencyPlansResponse { repeated FrequencyPlanDescription frequency_plans = 1; } +message GetPhyVersionsRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Optional Band ID to filter the results. + // If unused, all supported Bands and their versions are returned. + string band_id = 1; +} + +message GetPhyVersionsResponse { + message VersionInfo { + string band_id = 1; + repeated PHYVersion phy_versions = 2; + } + repeated VersionInfo version_info = 2; +} + +message ListBandsRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Optional Band ID to filter the results. + // If unused, all supported Bands are returned. + string band_id = 1; + // Optional PHY version to filter the results. + // If unused, all supported versions are returned. + PHYVersion phy_version = 2 [(validate.rules).enum.defined_only = true]; +} + +message BandDescription { + string id = 1; + + message Beacon { + DataRateIndex data_rate_index = 1; + string coding_rate = 2; + reserved 3; // inverted_polarity + reserved 4; // compute_frequency + repeated uint64 frequencies = 5; + } + + Beacon beacon = 2; + reserved 3; // ping_slot_frequency + repeated uint64 ping_slot_frequencies = 32; + + uint32 max_uplink_channels = 4; + + message Channel { + uint64 frequency = 1; + DataRateIndex min_data_rate = 2; + DataRateIndex max_data_rate = 3; + } + + repeated Channel uplink_channels = 5; + uint32 max_downlink_channels = 6; + repeated Channel downlink_channels = 7; + + message SubBandParameters { + uint64 min_frequency = 1; + uint64 max_frequency = 2; + float duty_cycle = 3; + float max_eirp = 4; + } + + repeated SubBandParameters sub_bands = 8; + + message BandDataRate { + DataRate rate = 1; + reserved 2; // max_mac_payload_size + } + + map data_rates = 9; + + uint64 freq_multiplier = 10; + bool implements_cf_list = 11; + CFListType cf_list_type = 12; + google.protobuf.Duration receive_delay_1 = 13; + google.protobuf.Duration receive_delay_2 = 14; + google.protobuf.Duration join_accept_delay_1 = 15; + google.protobuf.Duration join_accept_delay_2 = 16; + uint64 max_fcnt_gap = 17; + bool supports_dynamic_adr = 18; + ADRAckLimitExponent adr_ack_limit = 19; + google.protobuf.Duration min_retransmit_timeout = 20; + google.protobuf.Duration max_retransmit_timeout = 21; + repeated float tx_offset = 22; + DataRateIndex max_adr_data_rate_index = 23; + + bool tx_param_setup_req_support = 24; + float default_max_eirp = 25; + reserved 26; // rx1_channel + reserved 27; // rx2_datarate + reserved 28; // generate_ch_masks + reserved 29; // parse_ch_mask + + message Rx2Parameters { + DataRateIndex data_rate_index = 1; + uint64 frequency = 2; + } + + Rx2Parameters default_rx2_parameters = 30; + + message DwellTime { + google.protobuf.BoolValue uplinks = 1; + google.protobuf.BoolValue downlinks = 2; + } + + DwellTime boot_dwell_time = 31; +} + +message ListBandsResponse { + message VersionedBandDescription { + map band = 1; + } + map descriptions = 1; +} + service Configuration { rpc ListFrequencyPlans(ListFrequencyPlansRequest) returns (ListFrequencyPlansResponse) { option (google.api.http) = { get: "/configuration/frequency-plans" }; } + + // Returns a list of supported LoRaWAN PHY Versions for the given Band ID. + rpc GetPhyVersions(GetPhyVersionsRequest) returns (GetPhyVersionsResponse) { + option (google.api.http) = { + get: "/configuration/phy-versions" + }; + } + + rpc ListBands(ListBandsRequest) returns (ListBandsResponse) { + option (google.api.http) = { + get: "/configuration/bands" + additional_bindings { + get: "/configuration/bands/{band_id}" + } + additional_bindings { + get: "/configuration/bands/{band_id}/{phy_version}" + } + }; + } } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/contact_info.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/contact_info.proto index b477e16df..f82c37e0c 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/contact_info.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/contact_info.proto @@ -14,7 +14,10 @@ syntax = "proto3"; +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; @@ -24,8 +27,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + enum ContactType { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "CONTACT_TYPE" }; CONTACT_TYPE_OTHER = 0; CONTACT_TYPE_ABUSE = 1; @@ -34,7 +40,7 @@ enum ContactType { } enum ContactMethod { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "CONTACT_METHOD" }; CONTACT_METHOD_OTHER = 0; CONTACT_METHOD_EMAIL = 1; @@ -42,33 +48,41 @@ enum ContactMethod { } message ContactInfo { - ContactType contact_type = 1; - ContactMethod contact_method = 2; - string value = 3; + option (thethings.flags.message) = { select: true, set: true }; + ContactType contact_type = 1 [(validate.rules).enum.defined_only = true]; + ContactMethod contact_method = 2 [(validate.rules).enum.defined_only = true]; + string value = 3 [(validate.rules).string.max_len = 256]; bool public = 4; - google.protobuf.Timestamp validated_at = 5 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp validated_at = 5; } message ContactInfoValidation { - string id = 1 [(gogoproto.customname) = "ID"]; - string token = 2; + string id = 1 [(validate.rules).string = {max_len: 64, min_len: 1}]; + string token = 2 [(validate.rules).string = {max_len: 64, min_len: 1}]; EntityIdentifiers entity = 3; repeated ContactInfo contact_info = 4; - google.protobuf.Timestamp created_at = 5 [(gogoproto.stdtime) = true]; - google.protobuf.Timestamp expires_at = 6 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp created_at = 5; + google.protobuf.Timestamp expires_at = 6; } +// The ContactInfoRegistry service, exposed by the Identity Server, is used for +// validating contact information of registered entities. +// +// The actual contact information can be managed with the different registry services: +// ApplicationRegistry, ClientRegistry, GatewayRegistry, OrganizationRegistry and UserRegistry. service ContactInfoRegistry { // Request validation for the non-validated contact info for the given entity. rpc RequestValidation(EntityIdentifiers) returns (ContactInfoValidation) { option (google.api.http) = { post: "/contact_info/validation" + body: "*" }; } // Validate confirms a contact info validation. rpc Validate(ContactInfoValidation) returns (google.protobuf.Empty) { option (google.api.http) = { patch: "/contact_info/validation" + body: "*" }; }; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/deviceclaimingserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/deviceclaimingserver.proto index a682b2fd7..5cd1dd05b 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/deviceclaimingserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/deviceclaimingserver.proto @@ -17,59 +17,162 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; +import "google/protobuf/struct.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/identifiers.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ClaimEndDeviceRequest { message AuthenticatedIdentifiers { - bytes join_eui = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; - bytes dev_eui = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "DevEUI"]; + // JoinEUI (or AppEUI) of the device to claim. + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + // DevEUI of the device to claim. + bytes dev_eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + // Authentication code to prove ownership. + // In the LoRa Alliance TR005 specification, this equals the OwnerToken. string authentication_code = 3 [(validate.rules).string.pattern = "^[A-Z0-9]{1,32}$"]; } oneof source_device { option (validate.required) = true; - AuthenticatedIdentifiers authenticated_identifiers = 1 [(gogoproto.customname) = "AuthenticatedIdentifiers"]; - bytes qr_code = 2 [(validate.rules).bytes = {min_len: 0, max_len: 1024}, (gogoproto.customname) = "QRCode"]; + // Authenticated identifiers. + AuthenticatedIdentifiers authenticated_identifiers = 1; + // Raw QR code contents. + bytes qr_code = 2 [(validate.rules).bytes = {min_len: 0, max_len: 1024}]; } // Application identifiers of the target end device. - ApplicationIdentifiers target_application_ids = 3 [(gogoproto.nullable) = false, (validate.rules).message.required = true, (gogoproto.customname) = "TargetApplicationIDs"]; + ApplicationIdentifiers target_application_ids = 3 [(validate.rules).message.required = true]; // End device ID of the target end device. If empty, use the source device ID. - string target_device_id = 4 [(gogoproto.customname) = "TargetDeviceID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$" , max_len: 36}]; + string target_device_id = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$" , max_len: 36}]; reserved 6; // target_join_eui // The address of the Network Server where the device will be registered. // If set and if the source device is currently registered on a Network Server, settings will be transferred. // If not set, the device shall not be registered on a Network Server. string target_network_server_address = 7 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The KEK label of the Network Server to use for wrapping network session keys. - string target_network_server_kek_label = 8 [(gogoproto.customname) = "TargetNetworkServerKEKLabel", (validate.rules).string.max_len = 2048]; + string target_network_server_kek_label = 8 [(validate.rules).string.max_len = 2048]; // The address of the Application Server where the device will be registered. // If set and if the source device is currently registered on an Application Server, settings will be transferred. // If not set, the device shall not be registered on an Application Server. string target_application_server_address = 9 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The KEK label of the Application Server to use for wrapping the application session key. - string target_application_server_kek_label = 10 [(gogoproto.customname) = "TargetApplicationServerKEKLabel", (validate.rules).string.max_len = 2048]; + string target_application_server_kek_label = 10 [(validate.rules).string.max_len = 2048]; // The AS-ID of the Application Server to use. - string target_application_server_id = 11 [(gogoproto.customname) = "TargetApplicationServerID", (validate.rules).string.max_len = 100]; + string target_application_server_id = 11 [(validate.rules).string.max_len = 100]; reserved 12; // target_join_server_address // Home NetID. - bytes target_net_id = 13 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID", (gogoproto.customname) = "TargetNetID"]; + bytes target_net_id = 13 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; // If set, invalidate the authentication code with which the device gets claimed. This prohibits subsequent claiming requests. bool invalidate_authentication_code = 5; } message AuthorizeApplicationRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string api_key = 2 [(gogoproto.customname) = "APIKey", (validate.rules).string.min_len = 1]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + string api_key = 2 [ + (validate.rules).string = { min_len: 1, max_len: 128 } + ]; +} + +message GetInfoByJoinEUIRequest { + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; +} + +message GetInfoByJoinEUIResponse { + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + // If set, this Join EUI is available for claiming on one of the configured Join Servers. + bool supports_claiming = 2; } +message GetClaimStatusResponse { + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + bytes home_net_id = 2 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + bytes home_ns_id = 3 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + + message VendorSpecific{ + uint32 organization_unique_identifier = 1; + // Vendor Specific data in JSON format. + google.protobuf.Struct data = 2; + } + + VendorSpecific vendor_specific = 4; +} + +// The EndDeviceClaimingServer service configures authorization to claim end devices registered in an application, +// and allows clients to claim end devices. service EndDeviceClaimingServer { - // Claims the end device by claim authentication code or QR code and transfers the device to the target application. + // Claims the end device on a Join Server by claim authentication code or QR code. rpc Claim(ClaimEndDeviceRequest) returns (EndDeviceIdentifiers) { option (google.api.http) = { post: "/edcs/claim", @@ -77,6 +180,33 @@ service EndDeviceClaimingServer { }; }; + // Unclaims the end device on a Join Server. + rpc Unclaim(EndDeviceIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/edcs/claim/{application_ids.application_id}/devices/{device_id}", + body: "*" + }; + }; + + // Return whether claiming is available for a given JoinEUI. + rpc GetInfoByJoinEUI(GetInfoByJoinEUIRequest) returns (GetInfoByJoinEUIResponse) { + option (google.api.http) = { + post: "/edcs/claim/info", + body: "*" + }; + }; + + // Gets the claim status of an end device. + rpc GetClaimStatus(EndDeviceIdentifiers) returns (GetClaimStatusResponse) { + option (google.api.http) = { + get: "/edcs/claim/{application_ids.application_id}/devices/{device_id}", + }; + }; + + // Authorize the End Device Claiming Server to claim devices registered in the given application. The application + // identifiers are the source application, where the devices are registered before they are claimed. + // The API key is used to access the application, find the device, verify the claim request and delete the end device + // from the source application. rpc AuthorizeApplication(AuthorizeApplicationRequest) returns (google.protobuf.Empty) { option (google.api.http) = { post: "/edcs/applications/{application_ids.application_id}/authorize", @@ -84,9 +214,104 @@ service EndDeviceClaimingServer { }; }; + // Unauthorize the End Device Claiming Server to claim devices in the given application. + // This reverts the authorization given with rpc AuthorizeApplication. rpc UnauthorizeApplication(ApplicationIdentifiers) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/edcs/applications/{application_id}/authorize" }; }; } + +message CUPSRedirection { + // CUPS URI for LoRa Basics Station CUPS redirection. + string target_cups_uri = 1 [(validate.rules).string = {uri:true, pattern: "^https", max_len: 256}]; + + // The key set in the gateway to authenticate itself. + string current_gateway_key = 2 [(validate.rules).string.max_len = 2048]; + + // Optional PEM encoded CA Root certificate. If this field is empty, DCS will attempt to dial the Target CUPS server and fetch the CA. + bytes target_cups_trust = 3; + message ClientTLS { + // PEM encoded Client Certificate. + bytes cert = 1 [(validate.rules).bytes.max_len = 8192]; + // PEM encoded Client Private Key. + bytes key = 2 [(validate.rules).bytes.max_len = 8192]; + } + // CUPS Credentials for the gateway. + oneof gateway_credentials{ + // TODO: Support mTLS (https://github.com/TheThingsNetwork/lorawan-stack/issues/137) + ClientTLS client_tls = 4; + // The Device Claiming Server will fill this field with a The Things Stack API Key. + string auth_token = 5 [(validate.rules).string.max_len = 2048]; + } +} + +message ClaimGatewayRequest { + message AuthenticatedIdentifiers { + bytes gateway_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + bytes authentication_code = 2 [(validate.rules).bytes.max_len = 2048]; + } + oneof source_gateway { + option (validate.required) = true; + AuthenticatedIdentifiers authenticated_identifiers = 1; + bytes qr_code = 2 [(validate.rules).bytes = {min_len: 0, max_len: 1024}]; + } + + // Collaborator to grant all rights on the target gateway. + OrganizationOrUserIdentifiers collaborator = 3 [(validate.rules).message.required = true]; + + // Gateway ID for the target gateway. This must be a unique value. + // If this is not set, the target ID for the target gateway will be set to ``. + string target_gateway_id = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + + // Target Gateway Server Address for the target gateway. + string target_gateway_server_address = 5 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + + // Parameters to set CUPS redirection for the gateway. + CUPSRedirection cups_redirection = 6; + + // Frequency plan ID of the target gateway. + // This equals the first element of the frequency_plan_ids field. + string target_frequency_plan_id = 7 [(validate.rules).string.max_len = 64]; +} + + +message AuthorizeGatewayRequest { + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + string api_key = 2 [(validate.rules).string.min_len = 1]; +} + +service GatewayClaimingServer { + // Claims a gateway by claim authentication code or QR code and transfers the gateway to the target user. + rpc Claim(ClaimGatewayRequest) returns (GatewayIdentifiers) { + option (google.api.http) = { + post: "/gcls/claim", + body: "*" + }; + }; + + // AuthorizeGateway allows a gateway to be claimed. + rpc AuthorizeGateway(AuthorizeGatewayRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/gcls/gateways/{gateway_ids.gateway_id}/authorize", + body: "*" + }; + }; + + // UnauthorizeGateway prevents a gateway from being claimed. + rpc UnauthorizeGateway(GatewayIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/gcls/gateways/{gateway_id}/authorize" + }; + }; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/devicerepository.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/devicerepository.proto new file mode 100644 index 000000000..18d7796ce --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/devicerepository.proto @@ -0,0 +1,481 @@ +// Copyright © 2020 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; +import "lorawan-stack/api/end_device.proto"; +import "lorawan-stack/api/messages.proto"; +import "lorawan-stack/api/identifiers.proto"; + +package ttn.lorawan.v3; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +message EndDeviceBrand { + // Brand identifier, as specified in the Device Repository. + string brand_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + // Brand name. + string name = 2; + + // Private Enterprise Number (PEN) assigned by IANA. + uint32 private_enterprise_number = 3; + // Organization Unique Identifiers (OUI) assigned by IEEE. + repeated string organization_unique_identifiers = 4 [(validate.rules).repeated.items.string.pattern = "[0-9A-F]{6}"]; + // VendorID managed by the LoRa Alliance, as defined in TR005. + uint32 lora_alliance_vendor_id = 5; + // Brand website URL. + string website = 6 [(validate.rules).string = {uri: true, ignore_empty: true}]; + // Contact email address. + string email = 7 [(validate.rules).string = {email: true, ignore_empty: true}]; + // Path to brand logo. + string logo = 8 [(validate.rules).string.pattern = "^$|^(([a-z0-9-]+\\/)+)?([a-z0-9_-]+\\.)+(png|svg)$"]; +} + +enum KeyProvisioning { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "KEY_PROVISIONING" }; + + // Unknown Key Provisioning. + KEY_PROVISIONING_UNKNOWN = 0 [(thethings.json.enum_value) = { value: "unknown" }]; + // Custom Key Provisioning. + KEY_PROVISIONING_CUSTOM = 1 [(thethings.json.enum_value) = { value: "custom" }]; + // Key Provisioning from the Global Join Server. + KEY_PROVISIONING_JOIN_SERVER = 2 [(thethings.json.enum_value) = { value: "join server" }]; + // Key Provisioning from Manifest. + KEY_PROVISIONING_MANIFEST = 3 [(thethings.json.enum_value) = { value: "manifest" }]; +} + +enum KeySecurity { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "KEY_SECURITY" }; + + // Unknown key security. + KEY_SECURITY_UNKNOWN = 0 [(thethings.json.enum_value) = { value: "unknown" }]; + // No key security. + KEY_SECURITY_NONE = 1 [(thethings.json.enum_value) = { value: "none" }]; + // Read Protected key security. + KEY_SECURITY_READ_PROTECTED = 2 [(thethings.json.enum_value) = { value: "read protected" }]; + // Key security using the Security Element. + KEY_SECURITY_SECURE_ELEMENT = 3 [(thethings.json.enum_value) = { value: "secure element" }]; +} + +message EndDeviceModel { + // Brand identifier, as defined in the Device Repository. + string brand_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + // Model identifier, as defined in the Device Repository. + string model_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + // Model name, as defined in the Device Repository. + string name = 3; + // Model description. + string description = 4; + + message HardwareVersion { + // Hardware version string. + string version = 1; + // Numberic hardware revision number. + uint32 numeric = 2; + // Hardware part number. + string part_number = 3; + } + // Available hardware versions. + repeated HardwareVersion hardware_versions = 5; + + message FirmwareVersion { + message Profile { + // Vendor ID of the profile, as defined in the Device Repository. + // If this value is set, the profile is loaded from this vendor's folder. + // If this value is not set, the profile is loaded from the current (end device's) vendor. + string vendor_id = 4 [(validate.rules).string = {pattern: "^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + // Profile identifier, as defined in the Device Repository. + string profile_id = 1 [(validate.rules).string = {pattern: "^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + // Whether the device is LoRaWAN certified. + bool lorawan_certified = 2; + // Payload formatter codec identifier, as defined in the Device Repository. + string codec_id = 3 [(validate.rules).string = {pattern: "^$|^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + } + // Firmware version string. + string version = 1; + // Numeric firmware revision number. + uint32 numeric = 2; + // Hardware versions supported by this firmware version. + repeated string supported_hardware_versions = 3 [(validate.rules).repeated.unique = true]; + // Device profiles for each supported region (band). + map profiles = 4; + } + // Available firmware versions. + repeated FirmwareVersion firmware_versions = 6; + // List of sensors included in the device. + repeated string sensors = 7 [(validate.rules).repeated.unique = true]; + message Dimensions { + // Device width (mm). + google.protobuf.FloatValue width = 1; + // Device height (mm). + google.protobuf.FloatValue height = 2; + // Device diameter (mm). + google.protobuf.FloatValue diameter = 3; + // Device length (mm). + google.protobuf.FloatValue length = 4; + } + // Device dimensions. + Dimensions dimensions = 8; + // Device weight (gram). + google.protobuf.FloatValue weight = 9; + message Battery { + // Whether the device battery can be replaced. + google.protobuf.BoolValue replaceable = 1; + // Battery type. + string type = 2; + } + // Device battery information. + Battery battery = 10; + message OperatingConditions { + message Limits { + // Min value of operating conditions range. + google.protobuf.FloatValue min = 1; + // Max value of operating conditions range. + google.protobuf.FloatValue max = 2; + } + // Temperature operating conditions (Celsius). + Limits temperature = 1; + // Relative humidity operating conditions (Fraction, in range [0, 1]). + Limits relative_humidity = 2; + } + // Device operating conditions. + OperatingConditions operating_conditions = 11; + // Device IP rating code. + string ip_code = 12; + // Supported key provisioning methods. + repeated KeyProvisioning key_provisioning = 13 [(validate.rules).repeated = { + unique: true, + items: {enum: {defined_only: true}}, + }]; + // Device key security. + KeySecurity key_security = 14 [(validate.rules).enum.defined_only = true]; + + message Photos { + // Main device photo. + string main = 1 [(validate.rules).string.pattern = "^$|^(([a-z0-9-]+\\/)+)?([a-z0-9_-]+\\.)+(png|jpg|jpeg)$"]; + // List of other device photos. + repeated string other = 2 [(validate.rules).repeated = { + unique: true, + items: {string: {pattern: "^$|^(([a-z0-9-]+\\/)+)?([a-z0-9_-]+\\.)+(png|jpg|jpeg)$"}}, + }]; + } + // Device photos. + Photos photos = 15; + message Videos { + // Link to main device video. + string main = 1 [(validate.rules).string.pattern = "^(?:https?:\\/\\/(?:www\\.)?youtu(?:be\\.com\\/watch\\?v=|\\.be\\/)(?:[\\w\\-_]*)(?:&(amp;)?[\\w\\?=]*)?)$|^(?:https?:\\/\\/(?:www\\.)?vimeo\\.com\\/(?:channels\\/(?:\\w+\\/)?|groups\\/([^\\/]*)\\/videos\\/|)(?:\\d+)(?:|\\/\\?))$"]; + // Links to other device videos. + repeated string other = 2 [(validate.rules).repeated = { + unique: true, + items: {string: {pattern: "^(?:https?:\\/\\/(?:www\\.)?youtu(?:be\\.com\\/watch\\?v=|\\.be\\/)(?:[\\w\\-_]*)(?:&(amp;)?[\\w\\?=]*)?)$|^(?:https?:\\/\\/(?:www\\.)?vimeo\\.com\\/(?:channels\\/(?:\\w+\\/)?|groups\\/([^\\/]*)\\/videos\\/|)(?:\\d+)(?:|\\/\\?))$"}}, + }]; + } + // Device videos. + Videos videos = 16; + + // Device information page URL. + string product_url = 17 [(validate.rules).string = {uri: true, ignore_empty: true}]; + // Device datasheet URL. + string datasheet_url = 18 [(validate.rules).string = {uri: true, ignore_empty: true}]; + + message Reseller { + // Reseller name. + string name = 1; + // Reseller regions. + repeated string region = 2; + // Reseller URL. + string url = 3 [(validate.rules).string = {uri: true, ignore_empty: true}]; + } + // Reseller URLs. + repeated Reseller resellers = 19; + + message Compliances { + message Compliance { + string body = 1; + string norm = 2; + string standard = 3; + string version = 4; + } + // List of safety standards the device is compliant with. + repeated Compliance safety = 1; + // List of radio equipment standards the device is compliant with. + repeated Compliance radio_equipment = 2; + } + // List of standards the device is compliant with. + Compliances compliances = 20; + + // List of any additional radios included in the device. + repeated string additional_radios = 21 [(validate.rules).repeated.unique = true]; +} + +message GetEndDeviceBrandRequest { + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // Brand identifier, as defined in the Device Repository. + string brand_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + + // Field mask paths. + google.protobuf.FieldMask field_mask = 3; +} + +message ListEndDeviceBrandsRequest { + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // Limit the number of results per page. + uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 3; + + // Order (for pagination) + string order_by = 4 [(validate.rules).string = { + in: ["", "brand_id", "-brand_id", "name", "-name"] + }]; + + // Search for brands matching a query string. + string search = 5 [(validate.rules).string = {max_len: 100}]; + + // Field mask paths. + google.protobuf.FieldMask field_mask = 6; +} + +message GetEndDeviceModelRequest { + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // Brand identifier, as defined in the Device Repository. + string brand_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + // Model identifier, as defined in the Device Repository. + string model_id = 3 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + + // Field mask paths. + google.protobuf.FieldMask field_mask = 4; +} + +message ListEndDeviceModelsRequest { + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // List end devices from a specific brand. + string brand_id = 2 [(validate.rules).string = {pattern: "^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$" , max_len: 36}]; + + // Limit the number of results per page. + uint32 limit = 3 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 4; + // Order end devices + string order_by = 5 [(validate.rules).string = { + in: ["", "brand_id", "-brand_id", "model_id", "-model_id", "name", "-name"] + }]; + + // List end devices matching a query string. + string search = 6 [(validate.rules).string = {max_len: 100}]; + + // Field mask paths. + google.protobuf.FieldMask field_mask = 7; +} + +message GetTemplateRequest { + // Identifiers to uniquely identify a LoRaWAN end device profile. + message EndDeviceProfileIdentifiers { + // VendorID managed by the LoRa Alliance, as defined in TR005. + uint32 vendor_id = 1 [(validate.rules).uint32 = {gte: 1}]; + + // ID of the LoRaWAN end device profile assigned by the vendor. + uint32 vendor_profile_id = 2; + } + + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // End device version information. + // Use either EndDeviceVersionIdentifiers or EndDeviceProfileIdentifiers. + EndDeviceVersionIdentifiers version_ids = 2; + + // End device profile identifiers. + EndDeviceProfileIdentifiers end_device_profile_ids = 3; +} + +message GetPayloadFormatterRequest { + // Application identifiers. + ApplicationIdentifiers application_ids = 1 [deprecated = true]; + + // End device version information. + EndDeviceVersionIdentifiers version_ids = 2; + + // Field mask paths. + google.protobuf.FieldMask field_mask = 3; +} + +message ListEndDeviceBrandsResponse { + repeated EndDeviceBrand brands = 1; +} + +message ListEndDeviceModelsResponse { + repeated EndDeviceModel models = 1; +} + +message EncodedMessagePayload { + uint32 f_port = 1 [(validate.rules).uint32.lte = 255]; + bytes frm_payload = 2; + repeated string warnings = 3 [(validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 100 } } + }]; + repeated string errors = 4 [(validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 100 } } + }]; +} + +message DecodedMessagePayload { + google.protobuf.Struct data = 1; + repeated string warnings = 2 [(validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 100 } } + }]; + repeated string errors = 3 [(validate.rules).repeated = { + max_items: 10, + items: { string: { max_len: 100 } } + }]; +} + +message MessagePayloadDecoder { + // Payload formatter type. + PayloadFormatter formatter = 1 [(validate.rules).enum.defined_only = true]; + // Parameter for the formatter, must be set together. + string formatter_parameter = 2; + + string codec_id = 3 [(validate.rules).string = {pattern: "^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$" , max_len: 36}]; + + message Example { + string description = 1 [(validate.rules).string.max_len = 200]; + EncodedMessagePayload input = 2; + DecodedMessagePayload output = 3; + } + repeated Example examples = 4 [(validate.rules).repeated.max_items = 20]; +} + +message MessagePayloadEncoder { + // Payload formatter type. + PayloadFormatter formatter = 1 [(validate.rules).enum.defined_only = true]; + // Parameter for the formatter, must be set together. + string formatter_parameter = 2; + + string codec_id = 3 [(validate.rules).string = {pattern: "^([a-z0-9](?:[-]?[a-z0-9]){2,}|)?$" , max_len: 36}]; + + message Example { + string description = 1 [(validate.rules).string.max_len = 200]; + DecodedMessagePayload input = 2; + EncodedMessagePayload output = 3; + } + repeated Example examples = 4 [(validate.rules).repeated.max_items = 20]; +} + +service DeviceRepository { + rpc ListBrands(ListEndDeviceBrandsRequest) returns (ListEndDeviceBrandsResponse) { + option (google.api.http) = { + get: "/dr/brands" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands" + } + }; + }; + + rpc GetBrand(GetEndDeviceBrandRequest) returns (EndDeviceBrand) { + option (google.api.http) = { + get: "/dr/brands/{brand_id}" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{brand_id}" + } + }; + } + + rpc ListModels(ListEndDeviceModelsRequest) returns (ListEndDeviceModelsResponse) { + option (google.api.http) = { + get: "/dr/models" + additional_bindings { + get: "/dr/brands/{brand_id}/models" + } + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/models" + } + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{brand_id}/models" + } + }; + }; + + rpc GetModel(GetEndDeviceModelRequest) returns (EndDeviceModel) { + option (google.api.http) = { + get: "/dr/brands/{brand_id}/models/{model_id}" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{brand_id}/models/{model_id}" + } + }; + } + + rpc GetTemplate(GetTemplateRequest) returns (EndDeviceTemplate) { + option (google.api.http) = { + get: "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template" + additional_bindings { + get: "/dr/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template" + } + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/template" + } + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/vendors/{end_device_profile_ids.vendor_id}/profiles/{end_device_profile_ids.vendor_profile_id}/template" + } + }; + }; + + rpc GetUplinkDecoder(GetPayloadFormatterRequest) returns (MessagePayloadDecoder) { + option (google.api.http) = { + get: "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/uplink/decoder" + } + }; + }; + + rpc GetDownlinkDecoder(GetPayloadFormatterRequest) returns (MessagePayloadDecoder) { + option (google.api.http) = { + get: "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/decoder" + } + }; + }; + + rpc GetDownlinkEncoder(GetPayloadFormatterRequest) returns (MessagePayloadEncoder) { + option (google.api.http) = { + get: "/dr/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder" + additional_bindings { + get: "/dr/applications/{application_ids.application_id}/brands/{version_ids.brand_id}/models/{version_ids.model_id}/{version_ids.firmware_version}/{version_ids.band_id}/formatters/downlink/encoder" + } + }; + }; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/email_messages.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/email_messages.proto new file mode 100644 index 000000000..8aa28eba4 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/email_messages.proto @@ -0,0 +1,28 @@ +// Copyright © 2022 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +import "lorawan-stack/api/client.proto"; +import "lorawan-stack/api/rights.proto"; + +package ttn.lorawan.v3; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// CreateClientEmailMessage is used as a wrapper for handling the email regarding the create client procedure. +message CreateClientEmailMessage { + CreateClientRequest create_client_request = 1; + APIKey api_key = 2; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device.proto index fd70129b0..2fdb26ae0 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device.proto @@ -16,30 +16,50 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; +import "lorawan-stack/api/enums.proto"; import "lorawan-stack/api/identifiers.proto"; -import "lorawan-stack/api/join.proto"; import "lorawan-stack/api/keys.proto"; import "lorawan-stack/api/lorawan.proto"; import "lorawan-stack/api/messages.proto"; -import "lorawan-stack/api/picture.proto"; import "lorawan-stack/api/metadata.proto"; +import "lorawan-stack/api/picture.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message Session { + option (thethings.flags.message) = { select: true, set: true }; reserved 1; // RFU: Session ID // Device Address, issued by the Network Server or chosen by device manufacturer in case of testing range (beginning with 00-03). // Known by Network Server, Application Server and Join Server. Owned by Network Server. - bytes dev_addr = 2 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr", (gogoproto.nullable) = false]; - SessionKeys keys = 3 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + bytes dev_addr = 2 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New4BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + SessionKeys keys = 3 [(validate.rules).message.required = true]; // Last uplink frame counter value used. Network Server only. Application Server assumes the Network Server checked it. uint32 last_f_cnt_up = 4; // Last network downlink frame counter value used. Network Server only. @@ -49,36 +69,41 @@ message Session { // Frame counter of the last confirmed downlink message sent. Network Server only. uint32 last_conf_f_cnt_down = 7; // Time when the session started. Network Server only. - google.protobuf.Timestamp started_at = 8 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp started_at = 8; // Queued Application downlink messages. Stored in Application Server and Network Server. repeated ApplicationDownlink queued_application_downlinks = 9; } +message BoolValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + bool value = 1; +} + // MACParameters represent the parameters of the device's MAC layer (active or desired). // This is used internally by the Network Server. message MACParameters { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // Maximum EIRP (dBm). - float max_eirp = 1 [(gogoproto.customname) = "MaxEIRP"]; + float max_eirp = 1; reserved 2; // Deprecated: uplink_dwell_time reserved 3; // Deprecated: downlink_dwell_time // ADR: data rate index to use. - DataRateIndex adr_data_rate_index = 4 [(gogoproto.customname) = "ADRDataRateIndex", (validate.rules).enum.defined_only = true]; + DataRateIndex adr_data_rate_index = 4 [(validate.rules).enum.defined_only = true]; // ADR: transmission power index to use. - uint32 adr_tx_power_index = 5 [(gogoproto.customname) = "ADRTxPowerIndex", (validate.rules).uint32.lte = 15]; + uint32 adr_tx_power_index = 5 [(validate.rules).uint32.lte = 15]; // ADR: number of retransmissions. - uint32 adr_nb_trans = 6 [(gogoproto.customname) = "ADRNbTrans", (validate.rules).uint32.lte = 15]; + uint32 adr_nb_trans = 6 [(validate.rules).uint32.lte = 15]; // ADR: number of messages to wait before setting ADRAckReq. // This field is deprecated, use adr_ack_limit_exponent instead. - uint32 adr_ack_limit = 7 [(gogoproto.customname) = "ADRAckLimit", deprecated = true]; + uint32 adr_ack_limit = 7 [deprecated = true]; // ADR: number of messages to wait after setting ADRAckReq and before changing TxPower or DataRate. // This field is deprecated, use adr_ack_delay_exponent instead. - uint32 adr_ack_delay = 8 [(gogoproto.customname) = "ADRAckDelay", deprecated = true]; + uint32 adr_ack_delay = 8 [deprecated = true]; // Rx1 delay (Rx2 delay is Rx1 delay + 1 second). RxDelay rx1_delay = 9 [(validate.rules).enum.defined_only = true]; // Data rate offset for Rx1. - uint32 rx1_data_rate_offset = 10 [(validate.rules).uint32.lte = 7]; + DataRateOffset rx1_data_rate_offset = 10 [(validate.rules).enum.defined_only = true]; // Data rate index for Rx2. DataRateIndex rx2_data_rate_index = 11 [(validate.rules).enum.defined_only = true]; // Frequency for Rx2 (Hz). @@ -91,17 +116,13 @@ message MACParameters { RejoinCountExponent rejoin_count_periodicity = 15 [(validate.rules).enum.defined_only = true]; // Frequency of the class B ping slot (Hz). uint64 ping_slot_frequency = 16 [(validate.rules).uint64 = {lte: 0, gte: 100000}]; - // Data rate index of the class B ping slot. - // This field is deprecated, use ping_slot_data_rate_index_value instead. - DataRateIndex ping_slot_data_rate_index = 17 [deprecated = true]; // Frequency of the class B beacon (Hz). uint64 beacon_frequency = 18 [(validate.rules).uint64 = {lte: 0, gte: 100000}]; message Channel { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // Uplink frequency of the channel (Hz). - uint64 uplink_frequency = 1 [(validate.rules).uint64.gte = 100000]; + uint64 uplink_frequency = 1 [(validate.rules).uint64 = {lte: 0, gte: 100000}]; // Downlink frequency of the channel (Hz). uint64 downlink_frequency = 2 [(validate.rules).uint64.gte = 100000]; // Index of the minimum data rate for uplink. @@ -116,64 +137,40 @@ message MACParameters { // Whether uplink dwell time is set (400ms). // If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). - google.protobuf.BoolValue uplink_dwell_time = 20; + BoolValue uplink_dwell_time = 20; // Whether downlink dwell time is set (400ms). // If this field is not set, then the value is either unknown or irrelevant(Network Server cannot modify it). - google.protobuf.BoolValue downlink_dwell_time = 21; + BoolValue downlink_dwell_time = 21; // ADR: number of messages to wait before setting ADRAckReq. - ADRAckLimitExponentValue adr_ack_limit_exponent = 22 [(gogoproto.customname) = "ADRAckLimitExponent"]; + ADRAckLimitExponentValue adr_ack_limit_exponent = 22; // ADR: number of messages to wait after setting ADRAckReq and before changing TxPower or DataRate. - ADRAckDelayExponentValue adr_ack_delay_exponent = 23 [(gogoproto.customname) = "ADRAckDelayExponent"]; + ADRAckDelayExponentValue adr_ack_delay_exponent = 23; // Data rate index of the class B ping slot. - //DataRateIndexValue ping_slot_data_rate_index_value = 24; -} - -message EndDeviceBrand { - string id = 1 [(gogoproto.customname) = "ID"]; - string name = 2; - string url = 3 [(gogoproto.customname) = "URL"]; - // Logos contains file names of brand logos. - repeated string logos = 4; -} - -message EndDeviceModel { - string brand_id = 1 [(gogoproto.customname) = "BrandID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string id = 2 [(gogoproto.customname) = "ID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string name = 3; -} - -// Identifies an end device model with version information. -message EndDeviceVersionIdentifiers { - string brand_id = 1 [(gogoproto.customname) = "BrandID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string model_id = 2 [(gogoproto.customname) = "ModelID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string hardware_version = 3; - string firmware_version = 4; + DataRateIndexValue ping_slot_data_rate_index_value = 24; } // Template for creating end devices. message EndDeviceVersion { - option (gogoproto.populate) = false; - // Version identifiers. - EndDeviceVersionIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + EndDeviceVersionIdentifiers ids = 1 [(validate.rules).message.required = true]; // LoRaWAN MAC version. - MACVersion lorawan_version = 2 [(gogoproto.customname) = "LoRaWANVersion", (validate.rules).enum.defined_only = true]; + MACVersion lorawan_version = 2 [(validate.rules).enum.defined_only = true]; // LoRaWAN PHY version. - PHYVersion lorawan_phy_version = 3 [(gogoproto.customname) = "LoRaWANPHYVersion", (validate.rules).enum.defined_only = true]; + PHYVersion lorawan_phy_version = 3 [(validate.rules).enum.defined_only = true]; // ID of the frequency plan used by this device. - string frequency_plan_id = 4 [(gogoproto.customname) = "FrequencyPlanID", (validate.rules).string.max_len = 64]; + string frequency_plan_id = 4 [(validate.rules).string.max_len = 64]; // Photos contains file names of device photos. - repeated string photos = 5; + repeated string photos = 5 [(validate.rules).repeated.max_items = 10]; // Whether the device supports class B. bool supports_class_b = 6; // Whether the device supports class C. bool supports_class_c = 7; // Default MAC layer settings of the device. - MACSettings default_mac_settings = 8 [(gogoproto.customname) = "DefaultMACSettings"]; + MACSettings default_mac_settings = 8; // Minimum frequency the device is capable of using (Hz). uint64 min_frequency = 9; // Maximum frequency the device is capable of using (Hz). @@ -184,13 +181,71 @@ message EndDeviceVersion { bool resets_join_nonces = 12; // Default formatters defining the payload formats for this end device. - MessagePayloadFormatters default_formatters = 13 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + MessagePayloadFormatters default_formatters = 13 [(validate.rules).message.required = true]; +} + +// Adaptive Data Rate settings. +message ADRSettings { + option (thethings.flags.message) = { select: true, set: true, semantical: true }; + // Configuration options for static ADR. + message StaticMode { + option (thethings.flags.message) = { select: true, set: true }; + // Data rate index to use. + DataRateIndex data_rate_index = 1 [(validate.rules).enum.defined_only = true]; + // Transmission power index to use. + uint32 tx_power_index = 2 [(validate.rules).uint32.lte = 15]; + // Number of retransmissions. + uint32 nb_trans = 3 [(validate.rules).uint32 = { gte: 1, lte: 15 }]; + } + + // Configuration options for dynamic ADR. + message DynamicMode { + option (thethings.flags.message) = { select: true, set: true }; + // The ADR margin (dB) tells the network server how much margin it should add in ADR requests. + // A bigger margin is less efficient, but gives a better chance of successful reception. + // If unset, the default value from Network Server configuration will be used. + google.protobuf.FloatValue margin = 1; + + // Minimum data rate index. + // If unset, the default value from Network Server configuration will be used. + DataRateIndexValue min_data_rate_index = 2; + // Maximum data rate index. + // If unset, the default value from Network Server configuration will be used. + DataRateIndexValue max_data_rate_index = 3; + + // Minimum transmission power index. + // If unset, the default value from Network Server configuration will be used. + google.protobuf.UInt32Value min_tx_power_index = 4 [(validate.rules).uint32.lte = 15]; + // Maximum transmission power index. + // If unset, the default value from Network Server configuration will be used. + google.protobuf.UInt32Value max_tx_power_index = 5 [(validate.rules).uint32.lte = 15]; + + // Minimum number of retransmissions. + // If unset, the default value from Network Server configuration will be used. + google.protobuf.UInt32Value min_nb_trans = 6 [(validate.rules).uint32 = {gte: 1, lte: 3}]; + // Maximum number of retransmissions. + // If unset, the default value from Network Server configuration will be used. + google.protobuf.UInt32Value max_nb_trans = 7 [(validate.rules).uint32 = {gte: 1, lte: 3}]; + } + + // Configuration options for cases in which ADR is to be disabled + // completely. + message DisabledMode { + option (thethings.flags.message) = { select: true, set: true }; + } + + oneof mode { + StaticMode static = 1; + DynamicMode dynamic = 2; + DisabledMode disabled = 3; + } } message MACSettings { + option (thethings.flags.message) = { select: true, set: true }; // Maximum delay for the device to answer a MAC request or a confirmed downlink frame. // If unset, the default value from Network Server configuration will be used. - google.protobuf.Duration class_b_timeout = 1 [(gogoproto.stdduration) = true]; + google.protobuf.Duration class_b_timeout = 1; // Periodicity of the class B ping slot. // If unset, the default value from Network Server configuration will be used. PingSlotPeriodValue ping_slot_periodicity = 2; @@ -199,51 +254,51 @@ message MACSettings { DataRateIndexValue ping_slot_data_rate_index = 3; // Frequency of the class B ping slot (Hz). // If unset, the default value from Network Server configuration will be used. - google.protobuf.UInt64Value ping_slot_frequency = 4 [(validate.rules).uint64.gte = 100000]; + ZeroableFrequencyValue ping_slot_frequency = 4; // Frequency of the class B beacon (Hz). // If unset, the default value from Network Server configuration will be used. - google.protobuf.UInt64Value beacon_frequency = 25 [(validate.rules).uint64.gte = 100000]; + ZeroableFrequencyValue beacon_frequency = 25; // Maximum delay for the device to answer a MAC request or a confirmed downlink frame. // If unset, the default value from Network Server configuration will be used. - google.protobuf.Duration class_c_timeout = 5 [(gogoproto.stdduration) = true]; + google.protobuf.Duration class_c_timeout = 5; // Class A Rx1 delay. // If unset, the default value from Network Server configuration or regional parameters specification will be used. RxDelayValue rx1_delay = 6; // Rx1 data rate offset. // If unset, the default value from Network Server configuration will be used. - google.protobuf.UInt32Value rx1_data_rate_offset = 7 [(validate.rules).uint32.lte = 7]; + DataRateOffsetValue rx1_data_rate_offset = 7; // Data rate index for Rx2. // If unset, the default value from Network Server configuration or regional parameters specification will be used. DataRateIndexValue rx2_data_rate_index = 8; // Frequency for Rx2 (Hz). // If unset, the default value from Network Server configuration or regional parameters specification will be used. - google.protobuf.UInt64Value rx2_frequency = 9 [(validate.rules).uint64.gte = 100000]; + FrequencyValue rx2_frequency = 9; // List of factory-preset frequencies. // If unset, the default value from Network Server configuration or regional parameters specification will be used. - repeated uint64 factory_preset_frequencies = 10; + repeated uint64 factory_preset_frequencies = 10 [(validate.rules).repeated.max_items = 96]; // Maximum uplink duty cycle (of all channels). AggregatedDutyCycleValue max_duty_cycle = 11; // Whether the device supports 32-bit frame counters. // If unset, the default value from Network Server configuration will be used. - google.protobuf.BoolValue supports_32_bit_f_cnt = 12 [(gogoproto.customname) = "Supports32BitFCnt"]; + BoolValue supports_32_bit_f_cnt = 12; // Whether the Network Server should use ADR for the device. - // If unset, the default value from Network Server configuration will be used. - google.protobuf.BoolValue use_adr = 13 [(gogoproto.customname) = "UseADR"]; - // The ADR margin tells the network server how much margin it should add in ADR requests. + // This field is deprecated, use adr_settings instead. + BoolValue use_adr = 13 [deprecated = true]; + // The ADR margin (dB) tells the network server how much margin it should add in ADR requests. // A bigger margin is less efficient, but gives a better chance of successful reception. - // If unset, the default value from Network Server configuration will be used. - google.protobuf.FloatValue adr_margin = 14 [(gogoproto.customname) = "ADRMargin"]; + // This field is deprecated, use adr_settings.dynamic.margin instead. + google.protobuf.FloatValue adr_margin = 14 [deprecated = true]; // Whether the device resets the frame counters (not LoRaWAN compliant). // If unset, the default value from Network Server configuration will be used. - google.protobuf.BoolValue resets_f_cnt = 15; + BoolValue resets_f_cnt = 15; // The interval after which a DevStatusReq MACCommand shall be sent. // If unset, the default value from Network Server configuration will be used. - google.protobuf.Duration status_time_periodicity = 16 [(gogoproto.stdduration) = true]; + google.protobuf.Duration status_time_periodicity = 16; // Number of uplink messages after which a DevStatusReq MACCommand shall be sent. // If unset, the default value from Network Server configuration will be used. google.protobuf.UInt32Value status_count_periodicity = 17; @@ -253,13 +308,13 @@ message MACSettings { RxDelayValue desired_rx1_delay = 18; // The Rx1 data rate offset Network Server should configure device to use via MAC commands or Join-Accept. // If unset, the default value from Network Server configuration will be used. - google.protobuf.UInt32Value desired_rx1_data_rate_offset = 19; + DataRateOffsetValue desired_rx1_data_rate_offset = 19; // The Rx2 data rate index Network Server should configure device to use via MAC commands or Join-Accept. // If unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used. DataRateIndexValue desired_rx2_data_rate_index = 20; // The Rx2 frequency index Network Server should configure device to use via MAC commands. // If unset, the default value from frequency plan, Network Server configuration or regional parameters specification will be used. - google.protobuf.UInt64Value desired_rx2_frequency = 21 [(validate.rules).uint64.gte = 100000]; + FrequencyValue desired_rx2_frequency = 21; // The maximum uplink duty cycle (of all channels) Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration will be used. @@ -267,32 +322,52 @@ message MACSettings { // The ADR ACK limit Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration or regional parameters specification will be used. - ADRAckLimitExponentValue desired_adr_ack_limit_exponent = 23 [(gogoproto.customname) = "DesiredADRAckLimitExponent"]; + ADRAckLimitExponentValue desired_adr_ack_limit_exponent = 23; // The ADR ACK delay Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration or regional parameters specification will be used. - ADRAckDelayExponentValue desired_adr_ack_delay_exponent = 24 [(gogoproto.customname) = "DesiredADRAckDelayExponent"]; + ADRAckDelayExponentValue desired_adr_ack_delay_exponent = 24; // The data rate index of the class B ping slot Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration will be used. DataRateIndexValue desired_ping_slot_data_rate_index = 27; // The frequency of the class B ping slot (Hz) Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration or regional parameters specification will be used. - google.protobuf.UInt64Value desired_ping_slot_frequency = 28 [(validate.rules).uint64.gte = 100000]; + ZeroableFrequencyValue desired_ping_slot_frequency = 28; // The frequency of the class B beacon (Hz) Network Server should configure device to use via MAC commands. // If unset, the default value from Network Server configuration will be used. - google.protobuf.UInt64Value desired_beacon_frequency = 29 [(validate.rules).uint64.gte = 100000]; + ZeroableFrequencyValue desired_beacon_frequency = 29; + // Maximum EIRP (dBm). + // If unset, the default value from regional parameters specification will be used. + DeviceEIRPValue desired_max_eirp = 30; + // The minimum duration passed before a network-initiated(e.g. Class B or C) downlink following an arbitrary downlink. + google.protobuf.Duration class_b_c_downlink_interval = 31; + + // Whether uplink dwell time is set (400ms). + // If unset, the default value from Network Server configuration or regional parameters specification will be used. + BoolValue uplink_dwell_time = 32; + // Whether downlink dwell time is set (400ms). + // If unset, the default value from Network Server configuration or regional parameters specification will be used. + BoolValue downlink_dwell_time = 33; + + // Adaptive Data Rate settings. + // If unset, the default value from Network Server configuration or regional parameters specification will be used. + ADRSettings adr = 34; + + // Whether or not downlink messages should be scheduled. + // This option can be used in order to disable any downlink interaction with the end device. It will affect all types + // of downlink messages: data and MAC downlinks, and join accepts. + BoolValue schedule_downlinks = 35; } // MACState represents the state of MAC layer of the device. // MACState is reset on each join for OTAA or ResetInd for ABP devices. -// This is used internally by the Network Server and is read only. +// This is used internally by the Network Server. message MACState { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // Current LoRaWAN MAC parameters. - MACParameters current_parameters = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + MACParameters current_parameters = 1 [(validate.rules).message.required = true]; // Desired LoRaWAN MAC parameters. - MACParameters desired_parameters = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + MACParameters desired_parameters = 2 [(validate.rules).message.required = true]; // Currently active LoRaWAN device class // - Device class is A by default @@ -300,9 +375,9 @@ message MACState { // - If device sent DeviceModeInd MAC message, this will be set to that value Class device_class = 3 [(validate.rules).enum.defined_only = true]; // LoRaWAN MAC version. - MACVersion lorawan_version = 4 [(gogoproto.customname) = "LoRaWANVersion", (validate.rules).enum.defined_only = true]; + MACVersion lorawan_version = 4 [(validate.rules).enum.defined_only = true]; // Time when the last confirmed downlink message or MAC command was scheduled. - google.protobuf.Timestamp last_confirmed_downlink_at = 5 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_confirmed_downlink_at = 5; // Frame counter value of last uplink containing DevStatusAns. uint32 last_dev_status_f_cnt_up = 6; // Periodicity of the class B ping slot. @@ -316,26 +391,112 @@ message MACState { // Regenerated on each downlink. repeated MACCommand pending_requests = 10; + message JoinRequest { + option (thethings.flags.message) = { select: true, set: true }; + reserved 1,2,3,4,5,9,10; // deprecated JoinRequest fields. + DLSettings downlink_settings = 6 [(validate.rules).message.required = true]; + RxDelay rx_delay = 7 [(validate.rules).enum.defined_only = true]; + CFList cf_list = 8; + } message JoinAccept { + option (thethings.flags.message) = { select: true, set: true }; // Payload of the join-accept received from Join Server. bytes payload = 1 [(validate.rules).bytes = {min_len: 17, max_len: 33}]; - // JoinRequest sent to Join Server. - JoinRequest request = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + JoinRequest request = 2 [(validate.rules).message.required = true]; // Network session keys associated with the join. - SessionKeys keys = 3 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - repeated string correlation_ids = 4 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + SessionKeys keys = 3 [(validate.rules).message.required = true]; + repeated string correlation_ids = 4 [(validate.rules).repeated.items.string.max_len = 100]; + bytes dev_addr = 5 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New4BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + bytes net_id = 6 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; } + // Queued join-accept. // Set each time a (re-)join request accept is received from Join Server and removed each time a downlink is scheduled. JoinAccept queued_join_accept = 11; // Pending join request. - // Set each time a join accept is scheduled and removed each time an uplink is received from the device. + // Set each time a join-accept is scheduled and removed each time an uplink is received from the device. JoinRequest pending_join_request = 12; // Whether or not Rx windows are expected to be open. // Set to true every time an uplink is received. // Set to false every time a successful downlink scheduling attempt is made. bool rx_windows_available = 13; + // A minimal UplinkMessage definition which is binary compatible with the top level UplinkMessage message. + // Used for type safe recent uplink storage. + message UplinkMessage { + Message payload = 2 [(validate.rules).message.required = true]; + message TxSettings { + DataRate data_rate = 1 [(validate.rules).message.required = true]; + reserved 2 to 9; + } + TxSettings settings = 4 [(validate.rules).message.required = true]; + message RxMetadata { + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + float channel_rssi = 9; + float snr = 11; + DownlinkPathConstraint downlink_path_constraint = 14 [(validate.rules).enum.defined_only = true]; + bytes uplink_token = 15; + message PacketBrokerMetadata { + reserved 1 to 10; + } + PacketBrokerMetadata packet_broker = 18; + reserved 2 to 8, 10, 12, 13, 16, 17, 19, 20, 99; + } + repeated RxMetadata rx_metadata = 5; + google.protobuf.Timestamp received_at = 6; + repeated string correlation_ids = 7 [(validate.rules).repeated.items.string.max_len = 100]; + uint32 device_channel_index = 9 [(validate.rules).uint32 = {lte: 255}]; + reserved 1, 3, 8, 10; + } + + // A minimal DownlinkMessage definition which is binary compatible with the top level DownlinkMessage message. + // Used for type safe recent downlink storage. + message DownlinkMessage { + message Message { + message MHDR { + MType m_type = 1 [(validate.rules).enum.defined_only = true]; + reserved 2; + } + MHDR m_hdr = 1 [(validate.rules).message.required = true]; + message MACPayload { + uint32 f_port = 2 [(validate.rules).uint32.lte = 255]; + uint32 full_f_cnt = 5; + reserved 1, 3, 4; + } + MACPayload mac_payload = 3; + reserved 2, 4 to 7; + } + Message payload = 2; + repeated string correlation_ids = 6 [(validate.rules).repeated.items.string.max_len = 100]; + reserved 1, 3 to 5, 7; + } + // Recent data uplink messages sorted by time. // The number of messages stored may depend on configuration. repeated UplinkMessage recent_uplinks = 14; @@ -344,26 +505,47 @@ message MACState { repeated DownlinkMessage recent_downlinks = 15; // Time when the last network-initiated downlink message was scheduled. - google.protobuf.Timestamp last_network_initiated_downlink_at = 16 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_network_initiated_downlink_at = 16; // ADR Data rate index values rejected by the device. // Reset each time `current_parameters.channels` change. // Elements are sorted in ascending order. - repeated DataRateIndex rejected_adr_data_rate_indexes = 17 [(gogoproto.customname) = "RejectedADRDataRateIndexes", (validate.rules).repeated = { max_items: 15, items{ enum{ defined_only: true } } }]; + repeated DataRateIndex rejected_adr_data_rate_indexes = 17 [ (validate.rules).repeated = { max_items: 15, items{ enum{ defined_only: true } } }]; // ADR TX output power index values rejected by the device. // Elements are sorted in ascending order. - repeated uint32 rejected_adr_tx_power_indexes = 18 [(gogoproto.customname) = "RejectedADRTxPowerIndexes", (validate.rules).repeated = { max_items: 15, items{ uint32{ lte: 15 } } }]; + repeated uint32 rejected_adr_tx_power_indexes = 18 [(validate.rules).repeated = { max_items: 15, items{ uint32{ lte: 15 } } }]; // Frequencies rejected by the device. repeated uint64 rejected_frequencies = 19 [(validate.rules).repeated.items.uint64.gte = 100000]; // Time when the last downlink message was scheduled. - google.protobuf.Timestamp last_downlink_at = 20 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_downlink_at = 20; + + message DataRateRange { + option (thethings.flags.message) = { select: true, set: true }; + DataRateIndex min_data_rate_index = 1 [(validate.rules).enum.defined_only = true]; + DataRateIndex max_data_rate_index = 2 [(validate.rules).enum.defined_only = true]; + } + message DataRateRanges { + option (thethings.flags.message) = { select: true, set: true }; + repeated DataRateRange ranges = 1 [(validate.rules).repeated = { min_items: 1 }]; + } + // Data rate ranges rejected by the device per frequency. + map rejected_data_rate_ranges = 21; + + // Frame counter of uplink, which confirmed the last ADR parameter change. + uint32 last_adr_change_f_cnt_up = 22; + + // MAC command identifiers sent by the end device in the last received uplink. + // The Network Server may choose to store only certain types of MAC + // command identifiers in the underlying implementation. + repeated MACCommandIdentifier recent_mac_command_identifiers = 23; } // Power state of the device. enum PowerState { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "POWER" }; POWER_UNKNOWN = 0; POWER_BATTERY = 1; POWER_EXTERNAL = 2; @@ -371,22 +553,27 @@ enum PowerState { // Authentication code for end devices. message EndDeviceAuthenticationCode { - option (gogoproto.populate) = false; - - string value = 1 [(validate.rules).string.pattern = "^[A-Z0-9]{1,32}$"]; - google.protobuf.Timestamp valid_from = 2 [(gogoproto.stdtime) = true]; - google.protobuf.Timestamp valid_to = 3 [(gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + string value = 1 [(validate.rules).string.pattern = "^[a-zA-Z0-9]{1,32}$"]; + google.protobuf.Timestamp valid_from = 2; + google.protobuf.Timestamp valid_to = 3; } // Defines an End Device registration and its state on the network. // The persistence of the EndDevice is divided between the Network Server, Application Server and Join Server. // SDKs are responsible for combining (if desired) the three. message EndDevice { - option (gogoproto.populate) = false; - - EndDeviceIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + EndDeviceIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; // Friendly name of the device. Stored in Entity Registry. string name = 4 [(validate.rules).string.max_len = 50]; @@ -394,13 +581,19 @@ message EndDevice { string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this end device. Typically used for organizing end devices or for storing integration-specific data. Stored in Entity Registry. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Version Identifiers. Stored in Entity Registry, Network Server and Application Server. - EndDeviceVersionIdentifiers version_ids = 7 [(gogoproto.customname) = "VersionIDs"]; + EndDeviceVersionIdentifiers version_ids = 7; // Default service profile. Stored in Entity Registry. - string service_profile_id = 8 [(gogoproto.customname) = "ServiceProfileID", (validate.rules).string.max_len = 64]; + string service_profile_id = 8 [(validate.rules).string.max_len = 64]; // The address of the Network Server where this device is supposed to be registered. // Stored in Entity Registry and Join Server. @@ -411,7 +604,7 @@ message EndDevice { string network_server_address = 9 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The KEK label of the Network Server to use for wrapping network session keys. // Stored in Join Server. - string network_server_kek_label = 47 [(gogoproto.customname) = "NetworkServerKEKLabel", (validate.rules).string.max_len = 2048]; + string network_server_kek_label = 47 [(validate.rules).string.max_len = 2048]; // The address of the Application Server where this device is supposed to be registered. // Stored in Entity Registry and Join Server. // The typical format of the address is "host:port". If the port is omitted, @@ -421,10 +614,10 @@ message EndDevice { string application_server_address = 10 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The KEK label of the Application Server to use for wrapping the application session key. // Stored in Join Server. - string application_server_kek_label = 48 [(gogoproto.customname) = "ApplicationServerKEKLabel", (validate.rules).string.max_len = 2048]; + string application_server_kek_label = 48 [(validate.rules).string.max_len = 2048]; // The AS-ID of the Application Server to use. // Stored in Join Server. - string application_server_id = 49 [(gogoproto.customname) = "ApplicationServerID", (validate.rules).string.max_len = 100]; + string application_server_id = 49 [(validate.rules).string.max_len = 100]; // The address of the Join Server where this device is supposed to be registered. // Stored in Entity Registry. // The typical format of the address is "host:port". If the port is omitted, @@ -447,13 +640,13 @@ message EndDevice { bool supports_class_c = 14; // LoRaWAN MAC version. Stored in Network Server. // Copied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any. - MACVersion lorawan_version = 15 [(gogoproto.customname) = "LoRaWANVersion", (validate.rules).enum.defined_only = true]; + MACVersion lorawan_version = 15 [(validate.rules).enum.defined_only = true]; // LoRaWAN PHY version. Stored in Network Server. // Copied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any. - PHYVersion lorawan_phy_version = 16 [(gogoproto.customname) = "LoRaWANPHYVersion", (validate.rules).enum.defined_only = true]; + PHYVersion lorawan_phy_version = 16 [(validate.rules).enum.defined_only = true]; // ID of the frequency plan used by this device. // Copied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any. - string frequency_plan_id = 17 [(gogoproto.customname) = "FrequencyPlanID", (validate.rules).string.max_len = 64]; + string frequency_plan_id = 17 [(validate.rules).string.max_len = 64]; // Minimum frequency the device is capable of using (Hz). Stored in Network Server. // Copied on creation from template identified by version_ids, if any or from the home Network Server device profile, if any. uint64 min_frequency = 18; @@ -471,13 +664,26 @@ message EndDevice { RootKeys root_keys = 22; // Home NetID. Stored in Join Server. - bytes net_id = 23 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID", (gogoproto.customname) = "NetID"]; + bytes net_id = 23 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; // Settings for how the Network Server handles MAC layer for this device. Stored in Network Server. - MACSettings mac_settings = 24 [(gogoproto.customname) = "MACSettings"]; + MACSettings mac_settings = 24; // MAC state of the device. Stored in Network Server. - MACState mac_state = 25 [(gogoproto.customname) = "MACState"]; + MACState mac_state = 25; // Pending MAC state of the device. Stored in Network Server. - MACState pending_mac_state = 44 [(gogoproto.customname) = "PendingMACState"]; + MACState pending_mac_state = 44; // Current session of the device. Stored in Network Server and Application Server. Session session = 26; // Pending session. Stored in Network Server and Application Server until RekeyInd is received. @@ -496,18 +702,21 @@ message EndDevice { uint32 last_join_nonce = 30; // Last Rejoin counter value used (type 0/2). // Stored in Join Server. - uint32 last_rj_count_0 = 31 [(gogoproto.customname) = "LastRJCount0"]; + uint32 last_rj_count_0 = 31; // Last Rejoin counter value used (type 1). // Stored in Join Server. - uint32 last_rj_count_1 = 32 [(gogoproto.customname) = "LastRJCount1"]; + uint32 last_rj_count_1 = 32; // Time when last DevStatus MAC command was received. // Stored in Network Server. - google.protobuf.Timestamp last_dev_status_received_at = 33 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_dev_status_received_at = 33; // The power state of the device; whether it is battery-powered or connected to an external power source. // Received via the DevStatus MAC command at status_received_at. // Stored in Network Server. - PowerState power_state = 34 [(validate.rules).enum.defined_only = true]; + PowerState power_state = 34 [ + (validate.rules).enum.defined_only = true, + (thethings.flags.field) = { set: false } + ]; // Latest-known battery percentage of the device. // Received via the DevStatus MAC command at last_dev_status_received_at or earlier. // Stored in Network Server. @@ -517,16 +726,10 @@ message EndDevice { // Stored in Network Server. int32 downlink_margin = 36; - // Recent uplink messages with ADR bit set to 1 sorted by time. Stored in Network Server. - // The field is reset each time an uplink message carrying MACPayload is received with ADR bit set to 0. - // The number of messages stored is in the range [0,20]; - repeated UplinkMessage recent_adr_uplinks = 37 [(gogoproto.customname) = "RecentADRUplinks"]; - // Recent uplink messages sorted by time. Stored in Network Server. - // The number of messages stored may depend on configuration. - repeated UplinkMessage recent_uplinks = 38; - // Recent downlink messages sorted by time. Stored in Network Server. - // The number of messages stored may depend on configuration. - repeated DownlinkMessage recent_downlinks = 39; + reserved 37; // Deprecated: recent_adr_uplinks + reserved 38; // Deprecated: recent_uplinks + reserved 39; // Deprecated: recent_downlinks + // Queued Application downlink messages. Stored in Application Server, // which sets them on the Network Server. // This field is deprecated and is always set equal to session.queued_application_downlinks. @@ -537,14 +740,17 @@ message EndDevice { MessagePayloadFormatters formatters = 41; // ID of the provisioner. Stored in Join Server. - string provisioner_id = 42 [(gogoproto.customname) = "ProvisionerID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + string provisioner_id = 42 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; // Vendor-specific provisioning data. Stored in Join Server. google.protobuf.Struct provisioning_data = 43; // Indicates whether this device represents a multicast group. bool multicast = 45; - // Authentication code to claim ownership of the end device. Stored in Join Server. + // Authentication code to claim ownership of the end device. + // From TTS v3.21.0 this field is stored in the Identity Server. + // For TTS versions < 3.21.0, this field is stored in the Join Server. + // The value stored on the Identity Server takes precedence. EndDeviceAuthenticationCode claim_authentication_code = 46; // Skip decryption of uplink payloads and encryption of downlink payloads. @@ -554,41 +760,97 @@ message EndDevice { // This field overrides the application-level setting. google.protobuf.BoolValue skip_payload_crypto_override = 52; - // next: 53; + // Timestamp when the device has been activated. Stored in the Entity Registry. + // This field is set by the Application Server when an end device sends + // its first uplink. + // The Application Server will use the field in order to avoid repeated + // calls to the Entity Registry. + // The field cannot be unset once set. + google.protobuf.Timestamp activated_at = 53; + + // Timestamp when a device uplink has been last observed. + // This field is set by the Application Server and stored in the Identity Server. + google.protobuf.Timestamp last_seen_at = 54; + + // next: 55; } message EndDevices { repeated EndDevice end_devices = 1; } +message DevAddrPrefix { + // DevAddr base. + bytes dev_addr = 1 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + // Number of most significant bits from dev_addr that are used as prefix. + uint32 length = 2; +} + message CreateEndDeviceRequest { - EndDevice end_device = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + EndDevice end_device = 1 [(validate.rules).message.required = true]; } message UpdateEndDeviceRequest { - EndDevice end_device = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + EndDevice end_device = 1 [(validate.rules).message.required = true]; // The names of the end device fields that should be updated. // See the API reference for which fields can be set on the different services. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; +} + +message BatchUpdateEndDeviceLastSeenRequest { + message EndDeviceLastSeenUpdate { + EndDeviceIdentifiers ids = 1 [(validate.rules).message.required = true]; + google.protobuf.Timestamp last_seen_at = 2; + } + + // The last seen timestamp needs to be set per end device. + repeated EndDeviceLastSeenUpdate updates = 1; } message GetEndDeviceRequest { - EndDeviceIdentifiers end_device_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; // The names of the end device fields that should be returned. // See the API reference for which fields can be returned by the different services. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message GetEndDeviceIdentifiersForEUIsRequest { - bytes join_eui = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; - bytes dev_eui = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "DevEUI"]; + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + bytes dev_eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } message ListEndDevicesRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + ApplicationIdentifiers application_ids = 1; // The names of the end device fields that should be returned. // See the API reference for which fields can be returned by the different services. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ @@ -601,15 +863,22 @@ message ListEndDevicesRequest { } message SetEndDeviceRequest { - EndDevice end_device = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + EndDevice end_device = 1 [(validate.rules).message.required = true]; // The names of the end device fields that should be updated. // See the API reference for which fields can be set on the different services. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; +} + +message ResetAndGetEndDeviceRequest { + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + // The names of the end device fields that should be returned. + // See the API reference for which fields can be returned by the different services. + google.protobuf.FieldMask field_mask = 2; } message EndDeviceTemplate { - EndDevice end_device = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + EndDevice end_device = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; string mapping_key = 3 [(validate.rules).string.max_len = 100]; } @@ -625,7 +894,10 @@ message EndDeviceTemplateFormats { message ConvertEndDeviceTemplateRequest { // ID of the format. - string format_id = 1 [(gogoproto.customname) = "FormatID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + string format_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; // Data to convert. bytes data = 2; + + // End device profile identifiers. + EndDeviceVersionIdentifiers end_device_version_ids = 3; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device_services.proto index 9e3c630b2..215b0184d 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/end_device_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/end_device.proto"; @@ -23,10 +24,26 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The EndDeviceRegistry service, exposed by the Identity Server, is used to manage +// end device registrations. +// +// After registering an end device, it also needs to be registered in +// the NsEndDeviceRegistry that is exposed by the Network Server, +// the AsEndDeviceRegistry that is exposed by the Application Server, +// and the JsEndDeviceRegistry that is exposed by the Join Server. +// +// Before deleting an end device it first needs to be deleted from the +// NsEndDeviceRegistry, the AsEndDeviceRegistry and the JsEndDeviceRegistry. service EndDeviceRegistry { // Create a new end device within an application. - // After creating a device in the EndDeviceRegistry (Identity Server), it still - // needs to be created in the NsEndDeviceRegistry, AsEndDeviceRegistry and JsEndDeviceRegistry. + // + // After registering an end device, it also needs to be registered in + // the NsEndDeviceRegistry that is exposed by the Network Server, + // the AsEndDeviceRegistry that is exposed by the Application Server, + // and the JsEndDeviceRegistry that is exposed by the Join Server. rpc Create(CreateEndDeviceRequest) returns (EndDevice) { option (google.api.http) = { post: "/applications/{end_device.ids.application_ids.application_id}/devices" @@ -55,7 +72,7 @@ service EndDeviceRegistry { }; }; - // Update the OAuth client, changing the fields specified by the field mask to the provided values. + // Update the end device, changing the fields specified by the field mask to the provided values. rpc Update(UpdateEndDeviceRequest) returns (EndDevice) { option (google.api.http) = { put: "/applications/{end_device.ids.application_ids.application_id}/devices/{end_device.ids.device_id}" @@ -63,9 +80,13 @@ service EndDeviceRegistry { }; }; + // Update the last seen timestamp for a batch of end devices. + rpc BatchUpdateLastSeen(BatchUpdateEndDeviceLastSeenRequest) returns (google.protobuf.Empty); + // Delete the end device with the given IDs. - // Before deleting an end device from the EndDeviceRegistry (the Identity Server), - // the device needs to be deleted from the JsEndDeviceRegistry, AsEndDeviceRegistry and NsEndDeviceRegistry. + // + // Before deleting an end device it first needs to be deleted from the + // NsEndDeviceRegistry, the AsEndDeviceRegistry and the JsEndDeviceRegistry. // This is NOT done automatically. rpc Delete(EndDeviceIdentifiers) returns (google.protobuf.Empty) { option (google.api.http) = { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/enums.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/enums.proto index 7bbf1ec27..1aade0c18 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/enums.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/enums.proto @@ -17,11 +17,15 @@ syntax = "proto3"; package ttn.lorawan.v3; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + enum DownlinkPathConstraint { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "DOWNLINK_PATH_CONSTRAINT" }; // Indicates that the gateway can be selected for downlink without constraints by the Network Server. DOWNLINK_PATH_CONSTRAINT_NONE = 0; @@ -33,7 +37,7 @@ enum DownlinkPathConstraint { // State enum defines states that an entity can be in. enum State { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "STATE" }; // Denotes that the entity has been requested and is pending review by an admin. STATE_REQUESTED = 0; @@ -61,4 +65,5 @@ enum ClusterRole { GATEWAY_CONFIGURATION_SERVER = 10; QR_CODE_GENERATOR = 11; PACKET_BROKER_AGENT = 12; + DEVICE_REPOSITORY = 13; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/error.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/error.proto index baf2fc241..348894a5a 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/error.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/error.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/protobuf/any.proto"; import "google/protobuf/struct.proto"; @@ -22,9 +23,13 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // Error details that are communicated over gRPC (and HTTP) APIs. // The messages (for translation) are stored as "error::". message ErrorDetails { + option (thethings.flags.message) = { select: true, set: false }; // Namespace of the error (typically the package name in The Things Stack). string namespace = 1; // Name of the error. @@ -37,12 +42,13 @@ message ErrorDetails { google.protobuf.Struct attributes = 4; // The correlation ID of the error can be used to correlate the error to stack // traces the network may (or may not) store about recent errors. - string correlation_id = 5 [(gogoproto.customname) = "CorrelationID"]; + string correlation_id = 5; // The error that caused this error. - ErrorDetails cause = 6; + ErrorDetails cause = 6 [ + (thethings.flags.field) = { select: false } + ]; // The status code of the error. uint32 code = 7; // The details of the error. repeated google.protobuf.Any details = 8; } - diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/events.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/events.proto index 906a48147..1ebb93d91 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/events.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/events.proto @@ -26,23 +26,42 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message Event { + // Name of the event. This can be used to find the (localized) event description. string name = 1; - google.protobuf.Timestamp time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (validate.rules).timestamp.required = true]; + // Time at which the event was triggered. + google.protobuf.Timestamp time = 2 [(validate.rules).timestamp.required = true]; + // Identifiers of the entity (or entities) involved. repeated EntityIdentifiers identifiers = 3; + // Optional data attached to the event. google.protobuf.Any data = 4; - repeated string correlation_ids = 5 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + // Correlation IDs can be used to find related events and actions such as API calls. + repeated string correlation_ids = 5 [(validate.rules).repeated.items.string.max_len = 100]; + // The origin of the event. Typically the hostname of the server that created it. string origin = 6; + // Event context, internal use only. map context = 7; // The event will be visible to a caller that has any of these rights. Rights visibility = 8; message Authentication { + // The type of authentication that was used. This is typically a bearer token. string type = 1; + // The type of token that was used. Common types are APIKey, AccessToken and SessionToken. string token_type = 2; - string token_id = 3 [(gogoproto.customname) = "TokenID"]; - string remote_ip = 4 [(gogoproto.customname) = "RemoteIP"]; + // The ID of the token that was used. + string token_id = 3; } + // Details on the authentication provided by the caller that triggered this event. Authentication authentication = 9; + // The IP address of the caller that triggered this event. + string remote_ip = 10; + // The IP address of the caller that triggered this event. + string user_agent = 11; + // The unique identifier of the event, assigned on creation. + string unique_id = 12; } message StreamEventsRequest { @@ -54,7 +73,24 @@ message StreamEventsRequest { // If not empty, this will return historical events after the given time when the stream starts. // If used in combination with "tail", the limit that is reached first, is used. // The availability of historical events depends on server support and retention policy. - google.protobuf.Timestamp after = 3 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp after = 3; + // If provided, this will filter events, so that only events with the given names are returned. + // Names can be provided as either exact event names (e.g. 'gs.up.receive'), + // or as regular expressions (e.g. '/^gs\..+/'). + repeated string names = 4; +} + +message FindRelatedEventsRequest { + string correlation_id = 1 [ + (validate.rules).string = { + min_len: 1, + max_len: 100, + } + ]; +} + +message FindRelatedEventsResponse { + repeated Event events = 1; } // The Events service serves events from the cluster. @@ -67,4 +103,10 @@ service Events { body: "*" }; }; + + rpc FindRelated(FindRelatedEventsRequest) returns (FindRelatedEventsResponse) { + option (google.api.http) = { + get: "/events/related" + }; + } } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway.proto index 748de7287..0f9b2766b 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway.proto @@ -14,8 +14,10 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/field_mask.proto"; import "google/protobuf/struct.proto"; @@ -25,38 +27,44 @@ import "lorawan-stack/api/enums.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/metadata.proto"; import "lorawan-stack/api/rights.proto"; +import "lorawan-stack/api/secrets.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message GatewayBrand { - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; string name = 2; - string url = 3 [(gogoproto.customname) = "URL"]; + string url = 3; // Logos contains file names of brand logos. repeated string logos = 4; } message GatewayModel { - string brand_id = 1 [(gogoproto.customname) = "BrandID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string id = 2 [(gogoproto.customname) = "ID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + string brand_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; + string id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; string name = 3; } // Identifies an end device model with version information. message GatewayVersionIdentifiers { - string brand_id = 1 [(gogoproto.customname) = "BrandID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string model_id = 2 [(gogoproto.customname) = "ModelID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - string hardware_version = 3; - string firmware_version = 4; + option (thethings.flags.message) = { select: true, set: true }; + string brand_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; + string model_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; + string hardware_version = 3 [(validate.rules).string.max_len = 32]; + string firmware_version = 4 [(validate.rules).string.max_len = 32]; } message GatewayRadio { bool enable = 1; string chip_type = 2; uint64 frequency = 3; - float rssi_offset = 4 [(gogoproto.customname) = "RSSIOffset"]; + float rssi_offset = 4; message TxConfiguration { uint64 min_frequency = 1; @@ -66,54 +74,88 @@ message GatewayRadio { TxConfiguration tx_configuration = 5; } -// Template for creating gateways. -message GatewayVersion { - option (gogoproto.populate) = false; - - // Version identifiers. - GatewayVersionIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - - // Photos contains file names of gateway photos. - repeated string photos = 2; - repeated GatewayRadio radios = 3; - uint32 clock_source = 7; +// Authentication code for claiming gateways. +message GatewayClaimAuthenticationCode { + option (thethings.flags.message) = { select: true, set: true }; + Secret secret = 1; + google.protobuf.Timestamp valid_from = 2; + google.protobuf.Timestamp valid_to = 3; } // Gateway is the message that defines a gateway on the network. message Gateway { - GatewayIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + // The identifiers of the gateway. These are public and can be seen by any authenticated user in the network. + GatewayIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + // When the gateway was created. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the gateway was last updated. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the gateway was deleted. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp deleted_at = 26 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // The name of the gateway. This information is public and can be seen by any authenticated user in the network. string name = 4 [(validate.rules).string.max_len = 50]; + // A description for the gateway. This information is public and can be seen by any authenticated user in the network. string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this gateway. Typically used for organizing gateways or for storing integration-specific data. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Contact information for this gateway. Typically used to indicate who to contact with technical/security questions about the gateway. - repeated ContactInfo contact_info = 7; + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; + + OrganizationOrUserIdentifiers administrative_contact = 30; + OrganizationOrUserIdentifiers technical_contact = 31; - GatewayVersionIdentifiers version_ids = 8 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayVersionIdentifiers version_ids = 8; // The address of the Gateway Server to connect to. - // The typical format of the address is "host:port". If the port is omitted, + // This information is public and can be seen by any authenticated user in the network if status_public is true. + // The typical format of the address is "scheme://host:port". The scheme is optional. If the port is omitted, // the normal port inference (with DNS lookup, otherwise defaults) is used. // The connection shall be established with transport layer security (TLS). // Custom certificate authorities may be configured out-of-band. - string gateway_server_address = 9 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + string gateway_server_address = 9 [(validate.rules).string.pattern = "^([a-z]{2,5}://)?(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; bool auto_update = 10; - string update_channel = 11; + string update_channel = 11 [(validate.rules).string.max_len = 128]; // Frequency plan ID of the gateway. + // This information is public and can be seen by any authenticated user in the network. + // DEPRECATED: use frequency_plan_ids. // This equals the first element of the frequency_plan_ids field. - string frequency_plan_id = 12 [(gogoproto.customname) = "FrequencyPlanID", (validate.rules).string.max_len = 64]; + string frequency_plan_id = 12 [(validate.rules).string.max_len = 64]; // Frequency plan IDs of the gateway. + // This information is public and can be seen by any authenticated user in the network. // The first element equals the frequency_plan_id field. - repeated string frequency_plan_ids = 20 [(gogoproto.customname) = "FrequencyPlanIDs", (validate.rules).repeated = { max_items: 8, items{ string{ max_len: 64 } } }]; + repeated string frequency_plan_ids = 20 [ + (validate.rules).repeated = { + max_items: 8, + items: { string: { max_len: 64 } } + } + ]; - repeated GatewayAntenna antennas = 13 [(gogoproto.nullable) = false]; + // Antennas of the gateway. Location information of the antennas is public and can be seen by any authenticated user in the network if location_public=true. + repeated GatewayAntenna antennas = 13 [ + (validate.rules).repeated.max_items = 8 + ]; // The status of this gateway may be publicly displayed. bool status_public = 14; // The location of this gateway may be publicly displayed. @@ -128,11 +170,40 @@ message Gateway { bool enforce_duty_cycle = 17; DownlinkPathConstraint downlink_path_constraint = 18 [(validate.rules).enum.defined_only = true]; // Adjust the time that GS schedules class C messages in advance. This is useful for gateways that have a known high latency backhaul, like 3G and satellite. - google.protobuf.Duration schedule_anytime_delay = 19 [(gogoproto.stdduration) = true, (gogoproto.nullable) = true]; - // update the location of this gateway from status messages + google.protobuf.Duration schedule_anytime_delay = 19; + // Update the location of this gateway from status messages. This only works for gateways connecting with authentication; gateways connected over UDP are not supported. bool update_location_from_status = 21; + // The LoRa Basics Station LNS secret. + // This is either an auth token (such as an API Key) or a TLS private certificate. + // Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. + Secret lbs_lns_secret = 22; + // The authentication code for gateway claiming. + // Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. + // The entire field must be used in RPCs since sub-fields are validated wrt to each other. Direct selection/update of sub-fields only are not allowed. + // Use the top level field mask `claim_authentication_code` even when updating single fields. + GatewayClaimAuthenticationCode claim_authentication_code = 23; + // CUPS URI for LoRa Basics Station CUPS redirection. + // The CUPS Trust field will be automatically fetched from the cert chain presented by the target server. + string target_cups_uri = 24 [(validate.rules).string = { uri: true, ignore_empty: true }]; + // CUPS Key for LoRa Basics Station CUPS redirection. + // If redirecting to another instance of TTS, use the CUPS API Key for the gateway on the target instance. + // Requires the RIGHT_GATEWAY_READ_SECRETS for reading and RIGHT_GATEWAY_WRITE_SECRETS for updating this value. + Secret target_cups_key = 25; + // Require an authenticated gateway connection. This prevents the gateway from using the UDP protocol and requires authentication when using other protocols. + bool require_authenticated_connection = 27; + + // LR-FHSS gateway capabilities. + message LRFHSS { + option (thethings.flags.message) = { select: true, set: true }; + // The gateway supports the LR-FHSS uplink channels. + bool supported = 1; + } + + LRFHSS lrfhss = 28; - // next: 22 + bool disable_packet_broker_forwarding = 29; + + // next: 32 } message Gateways { @@ -140,22 +211,34 @@ message Gateways { } message GetGatewayRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; // The names of the gateway fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message GetGatewayIdentifiersForEUIRequest { - bytes eui = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.nullable) = false, (gogoproto.customname) = "EUI"]; + bytes eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } message ListGatewaysRequest { + option (thethings.flags.message) = { select: false, set: true }; // By default we list all gateways the caller has rights on. // Set the user or the organization (not both) to instead list the gateways // where the user or organization is collaborator on. - OrganizationOrUserIdentifiers collaborator = 1; + OrganizationOrUserIdentifiers collaborator = 1 [ + (thethings.flags.field) = { hidden: true } + ]; // The names of the gateway fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ @@ -165,22 +248,31 @@ message ListGatewaysRequest { uint32 limit = 4 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 5; + // Only return recently deleted gateways. + bool deleted = 6; } message CreateGatewayRequest { - Gateway gateway = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Gateway gateway = 1 [(validate.rules).message.required = true]; // Collaborator to grant all rights on the newly created gateway. - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message UpdateGatewayRequest { - Gateway gateway = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Gateway gateway = 1 [(validate.rules).message.required = true]; // The names of the gateway fields that should be updated. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListGatewayAPIKeysRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: false, set: true }; + + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + // Order the results by this field path. + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "api_key_id", "-api_key_id", "name", "-name", "created_at", "-created_at", "expires_at", "-expires_at"] } + ]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. @@ -188,55 +280,85 @@ message ListGatewayAPIKeysRequest { } message GetGatewayAPIKeyRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; // Unique public identifier for the API key. - string key_id = 2 [(gogoproto.customname) = "KeyID"]; + string key_id = 2; } message CreateGatewayAPIKeyRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; string name = 2 [(validate.rules).string.max_len = 50]; - repeated Right rights = 3 [(validate.rules).repeated.items.enum.defined_only = true]; + repeated Right rights = 3 [ + (validate.rules).repeated = { + min_items: 1, + unique: true, + items: { enum: { defined_only: true } } + } + ]; + google.protobuf.Timestamp expires_at = 4 [(validate.rules).timestamp.gt_now = true]; } message UpdateGatewayAPIKeyRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - APIKey api_key = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + APIKey api_key = 2 [(validate.rules).message.required = true]; + // The names of the api key fields that should be updated. + google.protobuf.FieldMask field_mask = 3; } message ListGatewayCollaboratorsRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "id", "-id", "-rights", "rights"] } + ]; } message GetGatewayCollaboratorRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message SetGatewayCollaboratorRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - Collaborator collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + Collaborator collaborator = 2 [(validate.rules).message.required = true]; +} + +enum GatewayAntennaPlacement { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "PLACEMENT" }; + PLACEMENT_UNKNOWN = 0; + INDOOR = 1; + OUTDOOR = 2; } // GatewayAntenna is the message that defines a gateway antenna. message GatewayAntenna { - // gain is the antenna gain relative to this gateway, in dBi. + option (thethings.flags.message) = { select: true, set: true }; + // Antenna gain relative to the gateway, in dBi. float gain = 1; // location is the antenna's location. - Location location = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - map attributes = 3 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + Location location = 2; + map attributes = 3 [ + deprecated = true, + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; + GatewayAntennaPlacement placement = 4; } message GatewayStatus { // Current time of the gateway - google.protobuf.Timestamp time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (validate.rules).timestamp.required = true]; + google.protobuf.Timestamp time = 1; // Boot time of the gateway // - can be left out to save bandwidth; old value will be kept - google.protobuf.Timestamp boot_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp boot_time = 2; // Versions of gateway subsystems // - each field can be left out to save bandwidth; old value will be kept // - map keys are written in snake_case @@ -246,38 +368,60 @@ message GatewayStatus { // fpga: "48" // dsp: "27" // hal: "v2-3.5.0" - map versions = 3 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[_-]?[a-z0-9]){2,}$" , max_len: 36}]; + map versions = 3 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[_-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 128 } } + } + ]; // Location of each gateway's antenna // - if left out, server uses registry-set location as fallback - repeated Location antenna_locations = 4; + repeated Location antenna_locations = 4 [(validate.rules).repeated.max_items = 8]; // IP addresses of this gateway. // Repeated addresses can be used to communicate addresses of multiple interfaces (LAN, Public IP, ...). - repeated string ip = 5 [(gogoproto.customname) = "IP"]; + repeated string ip = 5 [ + (validate.rules).repeated = { + max_items: 10, + items: { string: { ip: true } } + } + ]; // Metrics // - can be used for forwarding gateway metrics such as temperatures or performance metrics // - map keys are written in snake_case - map metrics = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[_-]?[a-z0-9]){2,}$" , max_len: 36}]; + map metrics = 6 [ + (validate.rules).map = { + max_pairs: 32, + keys: { string: { pattern: "^[a-z0-9](?:[_-]?[a-z0-9]){2,}$", max_len: 36 } }, + } + ]; // Advanced metadata fields // - can be used for advanced information or experimental features that are not yet formally defined in the API // - field names are written in snake_case google.protobuf.Struct advanced = 99; } +// Remote Address of the Gateway, as seen by the Gateway Server. +message GatewayRemoteAddress { + string ip = 1; // IPv4 or IPv6 address. +} + // Connection stats as monitored by the Gateway Server. message GatewayConnectionStats { - google.protobuf.Timestamp connected_at = 1 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp connected_at = 1; + google.protobuf.Timestamp disconnected_at = 11; string protocol = 2; // Protocol used to connect (for example, udp, mqtt, grpc) - google.protobuf.Timestamp last_status_received_at = 3 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_status_received_at = 3; GatewayStatus last_status = 4; - google.protobuf.Timestamp last_uplink_received_at = 5 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_uplink_received_at = 5; uint64 uplink_count = 6; - google.protobuf.Timestamp last_downlink_received_at = 7 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp last_downlink_received_at = 7; uint64 downlink_count = 8; message RoundTripTimes { - google.protobuf.Duration min = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (validate.rules).duration.required = true]; - google.protobuf.Duration max = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (validate.rules).duration.required = true]; - google.protobuf.Duration median = 3 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (validate.rules).duration.required = true]; + google.protobuf.Duration min = 1 [(validate.rules).duration.required = true]; + google.protobuf.Duration max = 2 [(validate.rules).duration.required = true]; + google.protobuf.Duration median = 3 [(validate.rules).duration.required = true]; uint32 count = 4; } RoundTripTimes round_trip_times = 9; @@ -285,9 +429,16 @@ message GatewayConnectionStats { message SubBand { uint64 min_frequency = 1; uint64 max_frequency = 2; + // Duty-cycle limit of the sub-band as a fraction of time. float downlink_utilization_limit = 3; + // Utilization rate of the available duty-cycle. This value should not exceed downlink_utilization_limit. float downlink_utilization = 4; } // Statistics for each sub band. repeated SubBand sub_bands = 10; + + // Gateway Remote Address. + GatewayRemoteAddress gateway_remote_address = 12; + + // next: 13 } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_configuration.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_configuration.proto new file mode 100644 index 000000000..31c92d688 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_configuration.proto @@ -0,0 +1,47 @@ +// Copyright © 2022 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "google/api/annotations.proto"; +import "lorawan-stack/api/identifiers.proto"; + +package ttn.lorawan.v3; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +message GetGatewayConfigurationRequest { + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + string format = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + string type = 3 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + string filename = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-._]?[a-z0-9]){2,}$|^$", max_len: 36}]; +} + +message GetGatewayConfigurationResponse { + bytes contents = 1; +} + +service GatewayConfigurationService { + rpc GetGatewayConfiguration(GetGatewayConfigurationRequest) returns (GetGatewayConfigurationResponse) { + option (google.api.http) = { + additional_bindings { + get: "/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{filename}" + } + additional_bindings { + get: "/gcs/gateways/configuration/{gateway_ids.gateway_id}/{format}/{type}/{filename}" + } + }; + }; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_services.proto index 68faffe88..049107c62 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gateway_services.proto @@ -26,6 +26,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The GatewayRegistry service, exposed by the Identity Server, is used to manage +// gateway registrations. service GatewayRegistry { // Create a new gateway. This also sets the given organization or user as // first collaborator with all possible rights. @@ -83,8 +88,30 @@ service GatewayRegistry { delete: "/gateways/{gateway_id}" }; }; + + // Restore a recently deleted gateway. This does not restore the EUI, + // as that was released when deleting the gateway. + // + // Deployment configuration may specify if, and for how long after deletion, + // entities can be restored. + rpc Restore(GatewayIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/gateways/{gateway_id}/restore" + }; + }; + + // Purge the gateway. This will release both gateway ID and EUI for reuse. + // The gateway owner is responsible for clearing data from any (external) integrations + // that may store and expose data by gateway ID. + rpc Purge(GatewayIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/gateways/{gateway_id}/purge" + }; + }; } +// The GatewayAcces service, exposed by the Identity Server, is used to manage +// API keys and collaborators of gateways. service GatewayAccess { // List the rights the caller has on this gateway. rpc ListRights(GatewayIdentifiers) returns (Rights) { @@ -157,8 +184,8 @@ service GatewayAccess { } message PullGatewayConfigurationRequest { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + GatewayIdentifiers gateway_ids = 1; + google.protobuf.FieldMask field_mask = 2; } service GatewayConfigurator { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gatewayserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gatewayserver.proto index f02df41d0..d7b9e0596 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/gatewayserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/gatewayserver.proto @@ -20,8 +20,10 @@ import "google/api/annotations.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/error.proto"; +import "google/protobuf/field_mask.proto"; import "lorawan-stack/api/gateway.proto"; import "lorawan-stack/api/identifiers.proto"; +import "lorawan-stack/api/lorawan.proto"; import "lorawan-stack/api/messages.proto"; import "lorawan-stack/api/mqtt.proto"; import "lorawan-stack/api/regional.proto"; @@ -30,11 +32,16 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // GatewayUp may contain zero or more uplink messages and/or a status message for the gateway. message GatewayUp { - // UplinkMessages received by the gateway. + // Uplink messages received by the gateway. repeated UplinkMessage uplink_messages = 1; + // Gateway status produced by the gateway. GatewayStatus gateway_status = 2; + // A Tx acknowledgment or error. TxAcknowledgment tx_acknowledgment = 3; } @@ -46,17 +53,17 @@ message GatewayDown { // The GtwGs service connects a gateway to a Gateway Server. service GtwGs { - // Link the gateway to the Gateway Server. + // Link a gateway to the Gateway Server for streaming upstream messages and downstream messages. rpc LinkGateway(stream GatewayUp) returns (stream GatewayDown); - // GetConcentratorConfig associated to the gateway. + // Get configuration for the concentrator. rpc GetConcentratorConfig(google.protobuf.Empty) returns (ConcentratorConfig); - // Get the MQTT server address and the username for the gateway. + // Get connection information to connect an MQTT gateway. rpc GetMQTTConnectionInfo(GatewayIdentifiers) returns (MQTTConnectionInfo) { option (google.api.http) = { get: "/gs/gateways/{gateway_id}/mqtt-connection-info" }; }; - // Get the MQTTV2 server address and the username for the gateway. + // Get legacy connection information to connect a The Things Network Stack V2 MQTT gateway. rpc GetMQTTV2ConnectionInfo(GatewayIdentifiers) returns (MQTTConnectionInfo) { option (google.api.http) = { get: "/gs/gateways/{gateway_id}/mqttv2-connection-info" @@ -65,21 +72,43 @@ service GtwGs { } message ScheduleDownlinkResponse { - google.protobuf.Duration delay = 1 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false, (validate.rules).duration.required = true]; + // The amount of time between the message has been scheduled and it will be transmitted by the gateway. + google.protobuf.Duration delay = 1 [(validate.rules).duration.required = true]; + // Downlink path chosen by the Gateway Server. + DownlinkPath downlink_path = 2; + // Whether RX1 has been chosen for the downlink message. + // Both RX1 and RX2 can be used for transmitting the same message by the same gateway. + bool rx1 = 3; + // Whether RX2 has been chosen for the downlink message. + // Both RX1 and RX2 can be used for transmitting the same message by the same gateway. + bool rx2 = 4; } message ScheduleDownlinkErrorDetails { + // Errors per path when downlink scheduling failed. repeated ErrorDetails path_errors = 1; } // The NsGs service connects a Network Server to a Gateway Server. service NsGs { - // ScheduleDownlink instructs the Gateway Server to schedule a downlink message. + // Instructs the Gateway Server to schedule a downlink message. // The Gateway Server may refuse if there are any conflicts in the schedule or // if a duty cycle prevents the gateway from transmitting. rpc ScheduleDownlink(DownlinkMessage) returns (ScheduleDownlinkResponse); } +message BatchGetGatewayConnectionStatsRequest { + repeated GatewayIdentifiers gateway_ids = 1 [(validate.rules).repeated = { min_items: 1, max_items: 100 }]; + // The names of the gateway stats fields that should be returned. + // This mask will be applied on each entry returned. + google.protobuf.FieldMask field_mask = 2; +} + +message BatchGetGatewayConnectionStatsResponse { + // The map key is the gateway identifier. + map entries = 1; +} + service Gs { // Get statistics about the current gateway connection to the Gateway Server. // This is not persisted between reconnects. @@ -88,4 +117,15 @@ service Gs { get: "/gs/gateways/{gateway_id}/connection/stats" }; }; + + // Get statistics about gateway connections to the Gateway Server of a batch of gateways. + // This is not persisted between reconnects. + // Gateways that are not connected or are part of a different cluster are ignored. + // It is up to the client to make sure that the gateways are in the requested cluster. + rpc BatchGetGatewayConnectionStats(BatchGetGatewayConnectionStatsRequest) returns (BatchGetGatewayConnectionStatsResponse) { + option (google.api.http) = { + post: "/gs/gateways/connection/stats" + body: "*" + }; + }; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/identifiers.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/identifiers.proto index 1491d107e..e8961d7a2 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/identifiers.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/identifiers.proto @@ -16,69 +16,126 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; message ApplicationIdentifiers { - option (gogoproto.populate) = false; - - string application_id = 1 [(gogoproto.customname) = "ApplicationID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: true, set: true }; + string application_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } message ClientIdentifiers { - option (gogoproto.populate) = false; - - string client_id = 1 [(gogoproto.customname) = "ClientID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: true, set: true }; + string client_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } message EndDeviceIdentifiers { - option (gogoproto.populate) = false; - - string device_id = 1 [(gogoproto.customname) = "DeviceID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; - ApplicationIdentifiers application_ids = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: true, set: true }; + string device_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + ApplicationIdentifiers application_ids = 2 [(validate.rules).message.required = true]; // The LoRaWAN DevEUI. - bytes dev_eui = 4 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "DevEUI"]; + bytes dev_eui = 4 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; // The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices). - bytes join_eui = 5 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; + bytes join_eui = 5 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; // The LoRaWAN DevAddr. - bytes dev_addr = 6 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr"]; + bytes dev_addr = 6 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New4BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; } message GatewayIdentifiers { - option (gogoproto.populate) = false; - - string gateway_id = 1 [(gogoproto.customname) = "GatewayID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + option (thethings.flags.message) = { select: true, set: true }; + string gateway_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; // Secondary identifier, which can only be used in specific requests. - bytes eui = 2 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "EUI"]; + bytes eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } message OrganizationIdentifiers { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // This ID shares namespace with user IDs. - string organization_id = 1 [(gogoproto.customname) = "OrganizationID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + string organization_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; } message UserIdentifiers { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // This ID shares namespace with organization IDs. - string user_id = 1 [(gogoproto.customname) = "UserID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + string user_id = 1 [ + (validate.rules).string = { + pattern: "^[a-z0-9](?:[-]?[a-z0-9]){1,}$", // NOTE: User IDs allow a shorter minimum length than other IDs. + max_len: 36 + } + ]; // Secondary identifier, which can only be used in specific requests. - string email = 2; + string email = 2 [(thethings.flags.field).hidden = true]; } // OrganizationOrUserIdentifiers contains either organization or user identifiers. message OrganizationOrUserIdentifiers { + option (thethings.flags.message) = { select: true, set: true }; oneof ids { option (validate.required) = true; - OrganizationIdentifiers organization_ids = 1 [(gogoproto.customname) = "OrganizationIDs"]; - UserIdentifiers user_ids = 2 [(gogoproto.customname) = "UserIDs"]; + OrganizationIdentifiers organization_ids = 1; + UserIdentifiers user_ids = 2; } } @@ -86,17 +143,54 @@ message OrganizationOrUserIdentifiers { message EntityIdentifiers { oneof ids { option (validate.required) = true; - ApplicationIdentifiers application_ids = 1 [(gogoproto.customname) = "ApplicationIDs"]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs"]; - EndDeviceIdentifiers device_ids = 3 [(gogoproto.customname) = "DeviceIDs"]; - GatewayIdentifiers gateway_ids = 4 [(gogoproto.customname) = "GatewayIDs"]; - OrganizationIdentifiers organization_ids = 5 [(gogoproto.customname) = "OrganizationIDs"]; - UserIdentifiers user_ids = 6 [(gogoproto.customname) = "UserIDs"]; + ApplicationIdentifiers application_ids = 1; + ClientIdentifiers client_ids = 2; + EndDeviceIdentifiers device_ids = 3; + GatewayIdentifiers gateway_ids = 4; + OrganizationIdentifiers organization_ids = 5; + UserIdentifiers user_ids = 6; } } -// Combine the identifiers of multiple entities. -// The main purpose of this message is its use in events. -message CombinedIdentifiers { - repeated EntityIdentifiers entity_identifiers = 1; +// Identifies an end device model with version information. +message EndDeviceVersionIdentifiers { + option (thethings.flags.message) = { select: true, set: true }; + string brand_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; + string model_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; + string hardware_version = 3 [(validate.rules).string.max_len = 32]; + string firmware_version = 4 [(validate.rules).string.max_len = 32]; + string band_id = 5 [(validate.rules).string.max_len = 32]; + // VendorID managed by the LoRa Alliance, as defined in TR005. + uint32 vendor_id = 6; + // ID of the LoRaWAN end device profile assigned by the vendor. + uint32 vendor_profile_id = 7; + string serial_number = 8 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36, ignore_empty: true}]; +} + +// Identifies a Network Server. +message NetworkIdentifiers { + option (thethings.flags.message) = { select: true, set: false }; + // LoRa Alliance NetID. + bytes net_id = 1 [ + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + }, + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + } + ]; + // Optional tenant identifier for multi-tenant deployments. + string tenant_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // Cluster identifier of the Network Server. + string cluster_id = 3 [(validate.rules).string.max_len = 64]; + // Cluster address of the Network Server. + string cluster_address = 4 [(validate.rules).string.max_len = 256]; + // Optional tenant address for multi-tenant deployments. + string tenant_address = 5 [(validate.rules).string.max_len = 256]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/identityserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/identityserver.proto index 0fa03ac58..2fc104c02 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/identityserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/identityserver.proto @@ -29,19 +29,27 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message AuthInfoResponse { message APIKeyAccess { - APIKey api_key = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - EntityIdentifiers entity_ids = 2 [(gogoproto.customname) = "EntityIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; + APIKey api_key = 1 [(validate.rules).message.required = true]; + EntityIdentifiers entity_ids = 2 [(validate.rules).message.required = true]; + } + message GatewayToken { + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + repeated Right rights = 6; } oneof access_method { - AuthInfoResponse.APIKeyAccess api_key = 1 [(gogoproto.customname) = "APIKey"]; - OAuthAccessToken oauth_access_token = 2 [(gogoproto.customname) = "OAuthAccessToken"]; + AuthInfoResponse.APIKeyAccess api_key = 1; + OAuthAccessToken oauth_access_token = 2; // Warning: A user authorized by session cookie will be granted all // current and future rights. When using this auth type, the respective // handlers need to ensure thorough CSRF and CORS protection using // appropriate middleware. UserSession user_session = 5; + AuthInfoResponse.GatewayToken gateway_token = 6; } Rights universal_rights = 3; bool is_admin = 4; @@ -64,7 +72,7 @@ message IsConfiguration { message UserRegistration { message Invitation { google.protobuf.BoolValue required = 1; - google.protobuf.Duration token_ttl = 2 [(gogoproto.customname) = "TokenTTL", (gogoproto.stdduration) = true]; + google.protobuf.Duration token_ttl = 2; } Invitation invitation = 1; @@ -86,6 +94,8 @@ message IsConfiguration { google.protobuf.UInt32Value min_special = 5; } PasswordRequirements password_requirements = 4; + + bool enabled = 5; } UserRegistration user_registration = 3; @@ -107,6 +117,18 @@ message IsConfiguration { google.protobuf.BoolValue create_organizations = 4; } UserRights user_rights = 6; + message UserLogin { + google.protobuf.BoolValue disable_credentials_login = 1; + } + UserLogin user_login = 7; + message AdminRights { + google.protobuf.BoolValue all = 1; + } + AdminRights admin_rights = 8; + reserved 9; reserved "network_limits"; + reserved 10; reserved "application_limits"; + reserved 11; reserved "organization_limits"; + reserved 12; reserved "user_limits"; } message GetIsConfigurationResponse { @@ -114,6 +136,8 @@ message GetIsConfigurationResponse { } service Is { + // Get the configuration of the Identity Server. The response is typically used + // to enable or disable features in a user interface. rpc GetConfiguration(GetIsConfigurationRequest) returns (GetIsConfigurationResponse) { option (google.api.http) = { get: "/is/configuration" diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/join.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/join.proto index e8ef59e9f..b188d621c 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/join.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/join.proto @@ -16,33 +16,57 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/duration.proto"; import "lorawan-stack/api/keys.proto"; import "lorawan-stack/api/lorawan.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; -message JoinRequest { - option (gogoproto.populate) = false; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; +message JoinRequest { bytes raw_payload = 1 [(validate.rules).bytes.len = 23]; Message payload = 2; - bytes dev_addr = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr"]; - MACVersion selected_mac_version = 4 [(gogoproto.customname) = "SelectedMACVersion"]; - bytes net_id = 5 [(gogoproto.nullable) = false, (gogoproto.customname) = "NetID", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID"]; - DLSettings downlink_settings = 6 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + bytes dev_addr = 3 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + MACVersion selected_mac_version = 4; + bytes net_id = 5 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + DLSettings downlink_settings = 6 [(validate.rules).message.required = true]; RxDelay rx_delay = 7 [(validate.rules).enum.defined_only = true]; // Optional CFList. - CFList cf_list = 8 [(gogoproto.customname) = "CFList"]; + CFList cf_list = 8; reserved 9; // Reserved for CFListType. - repeated string correlation_ids = 10 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + repeated string correlation_ids = 10 [(validate.rules).repeated.items.string.max_len = 100]; + + // Consumed airtime for the transmission of the join request. Calculated by Network Server using the RawPayload size and the transmission settings. + google.protobuf.Duration consumed_airtime = 11; } message JoinResponse { bytes raw_payload = 1 [(validate.rules).bytes = {min_len: 17, max_len: 33}]; - SessionKeys session_keys = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Duration lifetime = 3 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; - repeated string correlation_ids = 4 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + SessionKeys session_keys = 2 [(validate.rules).message.required = true]; + google.protobuf.Duration lifetime = 3; + repeated string correlation_ids = 4 [(validate.rules).repeated.items.string.max_len = 100]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/joinserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/joinserver.proto index 309a9a3b3..b690ad69d 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/joinserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/joinserver.proto @@ -16,93 +16,188 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; import "google/protobuf/struct.proto"; import "lorawan-stack/api/end_device.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/join.proto"; import "lorawan-stack/api/keys.proto"; import "lorawan-stack/api/lorawan.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message SessionKeyRequest { // Join Server issued identifier for the session keys. - bytes session_key_id = 1 [(gogoproto.customname) = "SessionKeyID", (validate.rules).bytes.max_len = 2048]; + bytes session_key_id = 1 [(validate.rules).bytes.max_len = 2048]; // LoRaWAN DevEUI. - bytes dev_eui = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "DevEUI"]; + bytes dev_eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; // The LoRaWAN JoinEUI (AppEUI until LoRaWAN 1.0.3 end devices). - bytes join_eui = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; + bytes join_eui = 3 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } message NwkSKeysResponse { // The (encrypted) Forwarding Network Session Integrity Key (or Network Session Key in 1.0 compatibility mode). - KeyEnvelope f_nwk_s_int_key = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + KeyEnvelope f_nwk_s_int_key = 1 [(validate.rules).message.required = true]; // The (encrypted) Serving Network Session Integrity Key. - KeyEnvelope s_nwk_s_int_key = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + KeyEnvelope s_nwk_s_int_key = 2 [(validate.rules).message.required = true]; // The (encrypted) Network Session Encryption Key. - KeyEnvelope nwk_s_enc_key = 3 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + KeyEnvelope nwk_s_enc_key = 3 [(validate.rules).message.required = true]; } // The NsJs service connects a Network Server to a Join Server. service NsJs { + // Handle a join-request message. rpc HandleJoin(JoinRequest) returns (JoinResponse); + // Request the network session keys for a particular session. rpc GetNwkSKeys(SessionKeyRequest) returns (NwkSKeysResponse); } message AppSKeyResponse { // The (encrypted) Application Session Key. - KeyEnvelope app_s_key = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + KeyEnvelope app_s_key = 1 [(validate.rules).message.required = true]; } // The AsJs service connects an Application Server to a Join Server. service AsJs { + // Request the application session key for a particular session. + rpc GetAppSKey(SessionKeyRequest) returns (AppSKeyResponse); +} + +// The AppJs service connects an Application to a Join Server. +service AppJs { + // Request the application session key for a particular session. rpc GetAppSKey(SessionKeyRequest) returns (AppSKeyResponse); } message CryptoServicePayloadRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - MACVersion lorawan_version = 2 [(gogoproto.customname) = "LoRaWANVersion", (validate.rules).enum.defined_only = true]; - bytes payload = 3; - string provisioner_id = 4 [(gogoproto.customname) = "ProvisionerID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // End device identifiers for the cryptographic operation. + EndDeviceIdentifiers ids = 1 [(validate.rules).message.required = true]; + // LoRaWAN version to use for the cryptographic operation. + MACVersion lorawan_version = 2 [(validate.rules).enum.defined_only = true]; + // Raw input payload. + bytes payload = 3 [(validate.rules).bytes.max_len = 256]; + // Provisioner that provisioned the end device. + string provisioner_id = 4 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // Provisioning data for the provisioner. google.protobuf.Struct provisioning_data = 5; } message CryptoServicePayloadResponse { + // Raw output payload. bytes payload = 1; } message JoinAcceptMICRequest { - CryptoServicePayloadRequest payload_request = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - RejoinType join_request_type = 2 [(validate.rules).enum.defined_only = true]; - bytes dev_nonce = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevNonce"]; + // Request data for the cryptographic operation. + CryptoServicePayloadRequest payload_request = 1 [(validate.rules).message.required = true]; + // LoRaWAN join-request type. + JoinRequestType join_request_type = 2 [(validate.rules).enum.defined_only = true]; + // LoRaWAN DevNonce. + bytes dev_nonce = 3 [ + (validate.rules).bytes = { len: 2, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal2Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"ABCD\"" + } + ]; } message DeriveSessionKeysRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - MACVersion lorawan_version = 2 [(gogoproto.customname) = "LoRaWANVersion", (validate.rules).enum.defined_only = true]; - bytes join_nonce = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.JoinNonce"]; - bytes dev_nonce = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevNonce"]; - bytes net_id = 5 [(gogoproto.nullable) = false, (gogoproto.customname) = "NetID", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID"]; - string provisioner_id = 6 [(gogoproto.customname) = "ProvisionerID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // End device identifiers to use for key derivation. + // The DevAddr must be set in this request. The DevEUI may need to be set, depending on the provisioner. + EndDeviceIdentifiers ids = 1 [(validate.rules).message.required = true]; + // LoRaWAN key derivation scheme. + MACVersion lorawan_version = 2 [(validate.rules).enum.defined_only = true]; + // LoRaWAN JoinNonce (or AppNonce). + bytes join_nonce = 3 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"ABCDEF\"" + } + ]; + // LoRaWAN DevNonce. + bytes dev_nonce = 4 [ + (validate.rules).bytes = { len: 2, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal2Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"ABCD\"" + } + ]; + // LoRaWAN NetID. + bytes net_id = 5 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + // Provisioner that provisioned the end device. + string provisioner_id = 6 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // Provisioning data for the provisioner. google.protobuf.Struct provisioning_data = 7; } message GetRootKeysRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - string provisioner_id = 2 [(gogoproto.customname) = "ProvisionerID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // End device identifiers to request the root keys for. + EndDeviceIdentifiers ids = 1 [(validate.rules).message.required = true]; + // Provisioner that provisioned the end device. + string provisioner_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // Provisioning data for the provisioner. google.protobuf.Struct provisioning_data = 3; } // Service for network layer cryptographic operations. service NetworkCryptoService { + // Calculate the join-request message MIC. rpc JoinRequestMIC(CryptoServicePayloadRequest) returns (CryptoServicePayloadResponse); + // Calculate the join-accept message MIC. rpc JoinAcceptMIC(JoinAcceptMICRequest) returns (CryptoServicePayloadResponse); + // Encrypt the join-accept payload. rpc EncryptJoinAccept(CryptoServicePayloadRequest) returns (CryptoServicePayloadResponse); + // Encrypt the rejoin-accept payload. rpc EncryptRejoinAccept(CryptoServicePayloadRequest) returns (CryptoServicePayloadResponse); + // Derive network session keys (NwkSKey, or FNwkSKey, SNwkSKey and NwkSEncKey) rpc DeriveNwkSKeys(DeriveSessionKeysRequest) returns (NwkSKeysResponse); // Get the NwkKey. Crypto Servers may return status code FAILED_PRECONDITION when root keys are not exposed. rpc GetNwkKey(GetRootKeysRequest) returns (KeyEnvelope); @@ -110,6 +205,7 @@ service NetworkCryptoService { // Service for application layer cryptographic operations. service ApplicationCryptoService { + // Derive the application session key (AppSKey). rpc DeriveAppSKey(DeriveSessionKeysRequest) returns (AppSKeyResponse); // Get the AppKey. Crypto Servers may return status code FAILED_PRECONDITION when root keys are not exposed. rpc GetAppKey(GetRootKeysRequest) returns (KeyEnvelope); @@ -118,23 +214,59 @@ service ApplicationCryptoService { message ProvisionEndDevicesRequest { option deprecated = true; - ApplicationIdentifiers application_ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true]; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; // ID of the provisioner service as configured in the Join Server. - string provisioner_id = 2 [(gogoproto.customname) = "ProvisionerID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + string provisioner_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; // Vendor-specific provisioning data. bytes provisioning_data = 3; message IdentifiersList { - bytes join_eui = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; - repeated EndDeviceIdentifiers end_device_ids = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "EndDeviceIDs"]; + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + repeated EndDeviceIdentifiers end_device_ids = 2; } message IdentifiersRange { - bytes join_eui = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; // DevEUI to start issuing from. - bytes start_dev_eui = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "StartDevEUI"]; + bytes start_dev_eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } message IdentifiersFromData { - bytes join_eui = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } oneof end_devices { // List of device identifiers that will be provisioned. @@ -193,19 +325,115 @@ service JsEndDeviceRegistry { }; } +message ApplicationActivationSettings { + option (thethings.flags.message) = { select: true, set: true }; + // The KEK label to use for wrapping application keys. + string kek_label = 1 [(validate.rules).string.max_len = 2048]; + // The (encrypted) Key Encryption Key. + KeyEnvelope kek = 2; + // Home NetID. + bytes home_net_id = 3 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + // The AS-ID of the Application Server to use. + string application_server_id = 4 [(validate.rules).string.max_len = 100]; +} + +message GetApplicationActivationSettingsRequest { + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 2; +} + +message SetApplicationActivationSettingsRequest { + option (thethings.flags.message) = { select: true, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + ApplicationActivationSettings settings = 2 [(validate.rules).message.required = true]; + google.protobuf.FieldMask field_mask = 3; +} + +message DeleteApplicationActivationSettingsRequest { + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; +} + +// The ApplicationActivationSettingRegistry service allows clients to manage their application activation settings. +service ApplicationActivationSettingRegistry { + // Get returns application activation settings. + rpc Get(GetApplicationActivationSettingsRequest) returns (ApplicationActivationSettings) { + option (google.api.http) = { + get: "/js/applications/{application_ids.application_id}/settings" + }; + }; + + // Set creates or updates application activation settings. + rpc Set(SetApplicationActivationSettingsRequest) returns (ApplicationActivationSettings) { + option (google.api.http) = { + post: "/js/applications/{application_ids.application_id}/settings" + body: "*" + }; + }; + + // Delete deletes application activation settings. + rpc Delete(DeleteApplicationActivationSettingsRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/js/applications/{application_ids.application_id}/settings" + }; + }; +} + message JoinEUIPrefix { - bytes join_eui = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64", (gogoproto.customname) = "JoinEUI"]; + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; uint32 length = 2; } message JoinEUIPrefixes { - repeated JoinEUIPrefix prefixes = 1 [(gogoproto.nullable) = false]; + repeated JoinEUIPrefix prefixes = 1; +} + +message GetDefaultJoinEUIResponse { + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; } service Js { + // Request the JoinEUI prefixes that are configured for this Join Server. rpc GetJoinEUIPrefixes(google.protobuf.Empty) returns (JoinEUIPrefixes) { option (google.api.http) = { get: "/js/join_eui_prefixes" }; }; + + // Request the default JoinEUI that is configured for this Join Server. + rpc GetDefaultJoinEUI(google.protobuf.Empty) returns (GetDefaultJoinEUIResponse) { + option (google.api.http) = { + get: "/js/default_join_eui" + }; + }; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/keys.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/keys.proto index 6fb7e3364..379eca117 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/keys.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/keys.proto @@ -16,24 +16,51 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message KeyEnvelope { + option (thethings.flags.message) = { select: true, set: true }; // The unencrypted AES key. - bytes key = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.AES128Key"]; + bytes key = 1 [ + (validate.rules).bytes = { len: 16, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal16Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New16BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"0123456789ABCDEF0123456789ABCDEF\"" + } + ]; // The label of the RFC 3394 key-encryption-key (KEK) that was used to encrypt the key. - string kek_label = 2 [(gogoproto.customname) = "KEKLabel", (validate.rules).string.max_len = 2048]; - bytes encrypted_key = 3; + string kek_label = 2 [(validate.rules).string.max_len = 2048]; + bytes encrypted_key = 3 [ + (validate.rules).bytes.max_len = 1024, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; } // Root keys for a LoRaWAN device. // These are stored on the Join Server. message RootKeys { + option (thethings.flags.message) = { select: true, set: true }; // Join Server issued identifier for the root keys. - string root_key_id = 1 [(gogoproto.customname) = "RootKeyID", (validate.rules).string.max_len = 2048]; + string root_key_id = 1 [(validate.rules).string.max_len = 2048]; // The (encrypted) Application Key. KeyEnvelope app_key = 2; // The (encrypted) Network Key. @@ -43,11 +70,10 @@ message RootKeys { // Session keys for a LoRaWAN session. // Only the components for which the keys were meant, will have the key-encryption-key (KEK) to decrypt the individual keys. message SessionKeys { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // Join Server issued identifier for the session keys. // This ID can be used to request the keys from the Join Server in case the are lost. - bytes session_key_id = 1 [(gogoproto.customname) = "SessionKeyID", (validate.rules).bytes.max_len = 2048]; + bytes session_key_id = 1 [(validate.rules).bytes.max_len = 2048]; // The (encrypted) Forwarding Network Session Integrity Key (or Network Session Key in 1.0 compatibility mode). // This key is stored by the (forwarding) Network Server. KeyEnvelope f_nwk_s_int_key = 2; diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/lorawan.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/lorawan.proto index 637e39c44..6ba33d7c0 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/lorawan.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/lorawan.proto @@ -14,21 +14,26 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "lorawan-stack/api/identifiers.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; -message Message { - option (gogoproto.populate) = false; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; - MHDR m_hdr = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - bytes mic = 2 [(gogoproto.customname) = "MIC", (validate.rules).bytes.len = 4]; +// Message represents a LoRaWAN message +message Message { + MHDR m_hdr = 1 [(validate.rules).message.required = true]; + bytes mic = 2 [(validate.rules).bytes = { min_len: 0, max_len: 4 }]; // Payload represents either MACPayload, RejoinRequestPayload, JoinRequestPayload or JoinAcceptPayload // - MACPayload length is in range [7:M] bytes, where M is PHY specific. @@ -38,7 +43,7 @@ message Message { oneof Payload { option (validate.required) = true; - MACPayload mac_payload = 3 [(gogoproto.customname) = "MACPayload"]; + MACPayload mac_payload = 3; JoinRequestPayload join_request_payload = 4; JoinAcceptPayload join_accept_payload = 5; RejoinRequestPayload rejoin_request_payload = 6; @@ -48,6 +53,7 @@ message Message { } enum MType { + option (thethings.json.enum) = { marshal_as_string: true }; JOIN_REQUEST = 0; JOIN_ACCEPT = 1; UNCONFIRMED_UP = 2; @@ -59,39 +65,55 @@ enum MType { } enum Major { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "LORAWAN" }; LORAWAN_R1 = 0; } enum MACVersion { - option (gogoproto.goproto_enum_prefix) = false; - option (gogoproto.enum_stringer) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "MAC" }; + option (thethings.flags.enum) = { alias_map: "go.thethings.network/lorawan-stack/v3/pkg/ttnpb.MACVersion_customvalue" }; - MAC_UNKNOWN = 0; - MAC_V1_0 = 1; - MAC_V1_0_1 = 2; - MAC_V1_0_2 = 3; - MAC_V1_1 = 4; - MAC_V1_0_3 = 5; - MAC_V1_0_4 = 6; + MAC_UNKNOWN = 0 [(thethings.json.enum_value) = { aliases: ["unknown"] }]; + MAC_V1_0 = 1 [(thethings.json.enum_value) = { aliases: ["1.0", "1.0.0"] }]; + MAC_V1_0_1 = 2 [(thethings.json.enum_value) = { aliases: ["1.0.1"] }]; + MAC_V1_0_2 = 3 [(thethings.json.enum_value) = { aliases: ["1.0.2"] }]; + MAC_V1_1 = 4 [(thethings.json.enum_value) = { aliases: ["1.1", "1.1.0"] }]; + MAC_V1_0_3 = 5 [(thethings.json.enum_value) = { aliases: ["1.0.3"] }]; + MAC_V1_0_4 = 6 [(thethings.json.enum_value) = { aliases: ["1.0.4"] }]; } enum PHYVersion { - option (gogoproto.goproto_enum_prefix) = false; - option (gogoproto.enum_stringer) = false; - - PHY_UNKNOWN = 0; - PHY_V1_0 = 1; - PHY_V1_0_1 = 2; - PHY_V1_0_2_REV_A = 3; - PHY_V1_0_2_REV_B = 4; - PHY_V1_1_REV_A = 5; - PHY_V1_1_REV_B = 6; - PHY_V1_0_3_REV_A = 7; + option allow_alias = true; + option (thethings.json.enum) = { marshal_as_string: true }; + option (thethings.flags.enum) = { alias_map: "go.thethings.network/lorawan-stack/v3/pkg/ttnpb.PHYVersion_customvalue" }; + + PHY_UNKNOWN = 0 [(thethings.json.enum_value) = { aliases: ["unknown"] }]; + + PHY_V1_0 = 1 [(thethings.json.enum_value) = { aliases: ["1.0", "1.0.0", "V1_0"] }]; + TS001_V1_0 = 1; + PHY_V1_0_1 = 2 [(thethings.json.enum_value) = { aliases: ["1.0.1", "V1_0_1"] }]; + TS001_V1_0_1 = 2; + + PHY_V1_0_2_REV_A = 3 [(thethings.json.enum_value) = { aliases: ["1.0.2", "1.0.2-a", "V1_0_2", "V1_0_2_REV_A"] }]; + RP001_V1_0_2 = 3; + PHY_V1_0_2_REV_B = 4 [(thethings.json.enum_value) = { aliases: ["1.0.2-b", "V1_0_2_REV_B"] }]; + RP001_V1_0_2_REV_B = 4; + PHY_V1_1_REV_A = 5 [(thethings.json.enum_value) = { aliases: ["1.1-a", "1.1.0-a", "V1_1_REV_A"] }]; + RP001_V1_1_REV_A = 5; + PHY_V1_1_REV_B = 6 [(thethings.json.enum_value) = { aliases: ["1.1-b", "1.1.0-b", "V1_1_REV_B"] }]; + RP001_V1_1_REV_B = 6; + PHY_V1_0_3_REV_A = 7 [(thethings.json.enum_value) = { aliases: ["1.0.3-a", "V1_0_3_REV_A"] }]; + RP001_V1_0_3_REV_A = 7; + + RP002_V1_0_0 = 8; + RP002_V1_0_1 = 9; + RP002_V1_0_2 = 10; + RP002_V1_0_3 = 11; } enum DataRateIndex { - option (gogoproto.goproto_enum_prefix) = false; - option (gogoproto.enum_stringer) = false; + option (thethings.json.enum) = { marshal_as_number: true, prefix: "DATA_RATE" }; + option (thethings.flags.enum) = { alias_map: "go.thethings.network/lorawan-stack/v3/pkg/ttnpb.DataRateIndex_customvalue" }; DATA_RATE_0 = 0; DATA_RATE_1 = 1; @@ -111,92 +133,237 @@ enum DataRateIndex { DATA_RATE_15 = 15; } +enum DataRateOffset { + option (thethings.json.enum) = { marshal_as_number: true, prefix: "DATA_RATE_OFFSET" }; + option (thethings.flags.enum) = { alias_map: "go.thethings.network/lorawan-stack/v3/pkg/ttnpb.DataRateOffset_customvalue" }; + + DATA_RATE_OFFSET_0 = 0; + DATA_RATE_OFFSET_1 = 1; + DATA_RATE_OFFSET_2 = 2; + DATA_RATE_OFFSET_3 = 3; + DATA_RATE_OFFSET_4 = 4; + DATA_RATE_OFFSET_5 = 5; + DATA_RATE_OFFSET_6 = 6; + DATA_RATE_OFFSET_7 = 7; +} + message MHDR { MType m_type = 1 [(validate.rules).enum.defined_only = true]; Major major = 2 [(validate.rules).enum.defined_only = true]; } message MACPayload { - option (gogoproto.populate) = false; - - FHDR f_hdr = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + FHDR f_hdr = 1 [(validate.rules).message.required = true]; uint32 f_port = 2 [(validate.rules).uint32.lte = 255]; - bytes frm_payload = 3 [(gogoproto.customname) = "FRMPayload"]; + bytes frm_payload = 3; google.protobuf.Struct decoded_payload = 4; // Full 32-bit FCnt value. Used internally by Network Server. uint32 full_f_cnt = 5; } message FHDR { - option (gogoproto.populate) = false; - - bytes dev_addr = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr"]; - FCtrl f_ctrl = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + bytes dev_addr = 1 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New4BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + FCtrl f_ctrl = 2 [(validate.rules).message.required = true]; uint32 f_cnt = 3 [(validate.rules).uint32.lte = 65535]; bytes f_opts = 4 [(validate.rules).bytes.max_len = 15]; } message FCtrl { - bool adr = 1 [(gogoproto.customname) = "ADR"]; - bool adr_ack_req = 2 [(gogoproto.customname) = "ADRAckReq"]; // Only on uplink. + bool adr = 1; + bool adr_ack_req = 2; // Only on uplink. bool ack = 3; bool f_pending = 4; // Only on downlink. bool class_b = 5; // Only on uplink. } message JoinRequestPayload { - option (gogoproto.populate) = false; - - bytes join_eui = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "JoinEUI", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64"]; - bytes dev_eui = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "DevEUI", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64"]; - bytes dev_nonce = 3 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevNonce"]; -} + bytes join_eui = 1 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + bytes dev_eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + bytes dev_nonce = 3 [ + (validate.rules).bytes = { len: 2, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal2Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New2BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"ABCD\"" + } + ]; +} + +enum JoinRequestType { + option (thethings.json.enum) = { marshal_as_string: true }; + + REJOIN_CONTEXT = 0; // Resets DevAddr, Session Keys, Frame Counters, Radio Parameters. + REJOIN_SESSION = 1; // Equivalent to the initial JoinRequest. + REJOIN_KEYS = 2; // Resets DevAddr, Session Keys, Frame Counters, while keeping the Radio Parameters. + JOIN = 255; // Normal join-request. +} + +enum RejoinRequestType { + option (thethings.json.enum) = { marshal_as_string: true }; -enum RejoinType { CONTEXT = 0; // Resets DevAddr, Session Keys, Frame Counters, Radio Parameters. SESSION = 1; // Equivalent to the initial JoinRequest. KEYS = 2; // Resets DevAddr, Session Keys, Frame Counters, while keeping the Radio Parameters. } message RejoinRequestPayload { - option (gogoproto.populate) = false; - - RejoinType rejoin_type = 1 [(validate.rules).enum.defined_only = true]; - bytes net_id = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "NetID", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID"]; - bytes join_eui = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "JoinEUI", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64"]; - bytes dev_eui = 4 [(gogoproto.nullable) = false, (gogoproto.customname) = "DevEUI", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.EUI64"]; + RejoinRequestType rejoin_type = 1 [(validate.rules).enum.defined_only = true]; + bytes net_id = 2 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + bytes join_eui = 3 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + bytes dev_eui = 4 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New8BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; uint32 rejoin_cnt = 5; // Contains RJCount0 or RJCount1 depending on rejoin_type. } message JoinAcceptPayload { - option (gogoproto.populate) = false; - bytes encrypted = 1; - bytes join_nonce = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.JoinNonce"]; - bytes net_id = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "NetID", (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID"]; - bytes dev_addr = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr"]; - DLSettings dl_settings = 5 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + bytes join_nonce = 2 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"ABCDEF\"" + } + ]; + bytes net_id = 3 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New3BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; + bytes dev_addr = 4 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (thethings.flags.field) = { + set_flag_new_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.New4BytesFlag", + set_flag_getter_func: "go.thethings.network/lorawan-stack/v3/cmd/ttn-lw-cli/customflags.GetExactBytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + DLSettings dl_settings = 5 [(validate.rules).message.required = true]; RxDelay rx_delay = 6 [(validate.rules).enum.defined_only = true]; - CFList cf_list = 7 [(gogoproto.customname) = "CFList"]; + CFList cf_list = 7; } message DLSettings { - option (gogoproto.populate) = false; - - uint32 rx1_dr_offset = 1 [(gogoproto.customname) = "Rx1DROffset", (validate.rules).uint32.lte = 7]; - DataRateIndex rx2_dr = 2 [(gogoproto.customname) = "Rx2DR", (validate.rules).enum.defined_only = true]; + option (thethings.flags.message) = { select: true, set: true }; + DataRateOffset rx1_dr_offset = 1 [(validate.rules).enum.defined_only = true]; + DataRateIndex rx2_dr = 2 [(validate.rules).enum.defined_only = true]; // OptNeg is set if Network Server implements LoRaWAN 1.1 or greater. - bool opt_neg = 3 [(gogoproto.customname) = "OptNeg"]; + bool opt_neg = 3; } enum CFListType { + option (thethings.json.enum) = { marshal_as_string: true }; + FREQUENCIES = 0; CHANNEL_MASKS = 1; } message CFList { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; CFListType type = 1 [(validate.rules).enum.defined_only = true]; // Frequencies to be broadcasted, in hecto-Hz. // These values are broadcasted as 24 bits unsigned integers. @@ -209,13 +376,15 @@ message CFList { } enum Class { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "CLASS" }; + CLASS_A = 0; CLASS_B = 1; CLASS_C = 2; } enum TxSchedulePriority { + option (thethings.json.enum) = { marshal_as_string: true }; LOWEST = 0; LOW = 1; BELOW_NORMAL = 2; @@ -226,22 +395,35 @@ enum TxSchedulePriority { } message LoRaDataRate { + option (thethings.flags.message) = { select: true, set: false }; // Bandwidth (Hz). uint32 bandwidth = 1; uint32 spreading_factor = 2; + string coding_rate = 3; } message FSKDataRate { + option (thethings.flags.message) = { select: true, set: false }; // Bit rate (bps). uint32 bit_rate = 1; } +message LRFHSSDataRate { + option (thethings.flags.message) = { select: true, set: false }; + uint32 modulation_type = 1; + // Operating Channel Width (Hz). + uint32 operating_channel_width = 2; + string coding_rate = 3; +} + message DataRate { + option (thethings.flags.message) = { select: true, set: false }; oneof modulation { option (validate.required) = true; - LoRaDataRate lora = 1 [(gogoproto.customname) = "LoRa"]; - FSKDataRate fsk = 2 [(gogoproto.customname) = "FSK"]; + LoRaDataRate lora = 1; + FSKDataRate fsk = 2; + LRFHSSDataRate lrfhss = 3; }; } @@ -249,10 +431,10 @@ message DataRate { // This message is used on both uplink and downlink. // On downlink, this is a scheduled transmission. message TxSettings { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: false }; // Transmission settings for downlink. message Downlink{ + option (thethings.flags.message) = { select: true, set: false }; // Index of the antenna on which the uplink was received and/or downlink must be sent. uint32 antenna_index = 1; // Transmission power (dBm). Only on downlink. @@ -262,37 +444,47 @@ message TxSettings { } // Data rate. - DataRate data_rate = 1 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - // LoRaWAN data rate index. - DataRateIndex data_rate_index = 2 [(validate.rules).enum.defined_only = true]; - // LoRa coding rate. - string coding_rate = 3; + DataRate data_rate = 1 [(validate.rules).message.required = true]; + reserved 2; // data_rate_index + reserved 3; // coding_rate // Frequency (Hz). uint64 frequency = 4; // Send a CRC in the packet; only on uplink; on downlink, CRC should not be enabled. - bool enable_crc = 5 [(gogoproto.customname) = "EnableCRC"]; + bool enable_crc = 5; // Timestamp of the gateway concentrator when the uplink message was received, or when the downlink message should be transmitted (microseconds). // On downlink, set timestamp to 0 and time to null to use immediate scheduling. uint32 timestamp = 6; // Time of the gateway when the uplink message was received, or when the downlink message should be transmitted. // For downlink, this requires the gateway to have GPS time synchronization. - google.protobuf.Timestamp time = 7 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp time = 7; // Transmission settings for downlink. Downlink downlink = 8; + // Concentrator timestamp for the downlink as calculated by the Gateway Server scheduler. + // This value takes into account necessary offsets such as the RTT (Round Trip Time) and TOA (Time Of Arrival). + // This field is set and used only by the Gateway Server. + int64 concentrator_timestamp = 9; } message GatewayAntennaIdentifiers { - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; + uint32 antenna_index = 2; +} + +message ClassBCGatewayIdentifiers { + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; uint32 antenna_index = 2; + uint32 group_index = 3; } message UplinkToken { - GatewayAntennaIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayAntennaIdentifiers ids = 1 [(validate.rules).message.required = true]; uint32 timestamp = 2; // Absolute time observed by the server when the uplink message has been received. - google.protobuf.Timestamp server_time = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp server_time = 3; // Absolute concentrator time as observed by the Gateway Server, accounting for rollovers. int64 concentrator_time = 4; + // Absolute time observed by the gateway when the uplink has been received. + google.protobuf.Timestamp gateway_time = 5; } message DownlinkPath { @@ -311,8 +503,6 @@ message DownlinkPath { // If the gateway has a scheduler, this request is sent to the gateway, in the order of gateway_ids. // Otherwise, the Gateway Server attempts to schedule the request and creates the TxSettings. message TxRequest { - option (gogoproto.populate) = false; - Class class = 1; // Downlink paths used to select a gateway for downlink. // In class A, the downlink paths are required to only contain uplink tokens. @@ -321,12 +511,14 @@ message TxRequest { // Rx1 delay (Rx2 delay is Rx1 delay + 1 second). RxDelay rx1_delay = 3 [(validate.rules).enum.defined_only = true]; - // LoRaWAN data rate index for Rx1. - DataRateIndex rx1_data_rate_index = 4 [(validate.rules).enum.defined_only = true]; + // LoRaWAN data rate for Rx1. + DataRate rx1_data_rate = 12; + reserved 4; // rx1_data_rate_index // Frequency (Hz) for Rx1. uint64 rx1_frequency = 5; - // LoRaWAN data rate index for Rx2. - DataRateIndex rx2_data_rate_index = 6 [(validate.rules).enum.defined_only = true]; + // LoRaWAN data rate for Rx2. + DataRate rx2_data_rate = 13; + reserved 6; // rx2_data_rate_index // Frequency (Hz) for Rx2. uint64 rx2_frequency = 7; // Priority for scheduling. @@ -338,10 +530,11 @@ message TxRequest { // This value is only valid for class C downlink; class A downlink uses uplink tokens and class B downlink is scheduled on ping slots. // This requires the gateway to have GPS time sychronization. // If the absolute time is not set, the first available time will be used that does not conflict or violate regional limitations. - google.protobuf.Timestamp absolute_time = 9 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp absolute_time = 9; // Frequency plan ID from which the frequencies in this message are retrieved. - string frequency_plan_id = 10 [(gogoproto.customname) = "FrequencyPlanID", (validate.rules).string.max_len = 64]; + string frequency_plan_id = 10 [(validate.rules).string.max_len = 64]; + reserved 11; // lorawan_phy_version // Advanced metadata fields // - can be used for advanced information or experimental features that are not yet formally defined in the API @@ -350,7 +543,7 @@ message TxRequest { } enum MACCommandIdentifier { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "CID" }; CID_RFU_0 = 0; CID_RESET = 1; @@ -376,30 +569,28 @@ enum MACCommandIdentifier { } message MACCommand { - option (gogoproto.populate) = false; - - MACCommandIdentifier cid = 1 [(gogoproto.customname) = "CID", (validate.rules).enum = {defined_only: true, not_in:[0]}]; + MACCommandIdentifier cid = 1 [(validate.rules).enum = {defined_only: true, not_in:[0]}]; oneof payload { bytes raw_payload = 2; ResetInd reset_ind = 3; ResetConf reset_conf = 4; LinkCheckAns link_check_ans = 5; - LinkADRReq link_adr_req = 6 [(gogoproto.customname) = "LinkADRReq"]; - LinkADRAns link_adr_ans = 7 [(gogoproto.customname) = "LinkADRAns"]; + LinkADRReq link_adr_req = 6; + LinkADRAns link_adr_ans = 7; DutyCycleReq duty_cycle_req = 8; RxParamSetupReq rx_param_setup_req = 9; RxParamSetupAns rx_param_setup_ans = 10; DevStatusAns dev_status_ans = 11; NewChannelReq new_channel_req = 12; NewChannelAns new_channel_ans = 13; - DLChannelReq dl_channel_req = 14 [(gogoproto.customname) = "DLChannelReq"]; - DLChannelAns dl_channel_ans = 15 [(gogoproto.customname) = "DLChannelAns"]; + DLChannelReq dl_channel_req = 14; + DLChannelAns dl_channel_ans = 15; RxTimingSetupReq rx_timing_setup_req = 16; TxParamSetupReq tx_param_setup_req = 17; RekeyInd rekey_ind = 18; RekeyConf rekey_conf = 19; - ADRParamSetupReq adr_param_setup_req = 20 [(gogoproto.customname) = "ADRParamSetupReq"]; + ADRParamSetupReq adr_param_setup_req = 20; DeviceTimeAns device_time_ans = 21; ForceRejoinReq force_rejoin_req = 22; RejoinParamSetupReq rejoin_param_setup_req = 23; @@ -415,25 +606,17 @@ message MACCommand { } message ResetInd { - option (gogoproto.populate) = false; - Minor minor_version = 1 [(validate.rules).enum = {defined_only: true, in:[1]}]; } message ResetConf { - option (gogoproto.populate) = false; - Minor minor_version = 1 [(validate.rules).enum = {defined_only: true, in:[1]}]; } message LinkCheckAns { - option (gogoproto.populate) = false; - // Indicates the link margin in dB of the received LinkCheckReq, relative to the demodulation floor. uint32 margin = 1 [(validate.rules).uint32.lte = 254]; uint32 gateway_count = 2 [(validate.rules).uint32.lte = 255]; } message LinkADRReq { - option (gogoproto.populate) = false; - DataRateIndex data_rate_index = 1 [(validate.rules).enum.defined_only = true]; uint32 tx_power_index = 2 [(validate.rules).uint32.lte = 15]; repeated bool channel_mask = 3 [(validate.rules).repeated.max_items = 16]; @@ -450,10 +633,8 @@ message MACCommand { AggregatedDutyCycle max_duty_cycle = 1 [(validate.rules).enum.defined_only = true]; } message RxParamSetupReq { - option (gogoproto.populate) = false; - DataRateIndex rx2_data_rate_index = 1 [(validate.rules).enum.defined_only = true]; - uint32 rx1_data_rate_offset = 2 [(validate.rules).uint32.lte = 7]; + DataRateOffset rx1_data_rate_offset = 2 [(validate.rules).enum.defined_only = true]; uint64 rx2_frequency = 3 [(validate.rules).uint64.gte = 100000]; // Rx2 frequency (Hz). } message RxParamSetupAns { @@ -462,8 +643,6 @@ message MACCommand { bool rx2_frequency_ack = 3; } message DevStatusAns { - option (gogoproto.populate) = false; - // Device battery status. // 0 indicates that the device is connected to an external power source. // 1..254 indicates a battery level. @@ -473,8 +652,6 @@ message MACCommand { int32 margin = 2 [(validate.rules).int32 = {gte: -32, lte: 31}]; } message NewChannelReq { - option (gogoproto.populate) = false; - uint32 channel_index = 1 [(validate.rules).uint32.lte = 255]; uint64 frequency = 2 [(validate.rules).uint64 = { lte: 0, gte: 100000 }]; // Channel frequency (Hz). DataRateIndex min_data_rate_index = 3 [(validate.rules).enum.defined_only = true]; @@ -485,8 +662,6 @@ message MACCommand { bool data_rate_ack = 2; } message DLChannelReq { - option (gogoproto.populate) = false; - uint32 channel_index = 1 [(validate.rules).uint32.lte = 255]; uint64 frequency = 2 [(validate.rules).uint64.gte = 100000]; // Downlink channel frequency (Hz). } @@ -500,7 +675,7 @@ message MACCommand { message TxParamSetupReq { // Indicates the maximum EIRP value in dBm, indexed by the following vector: // [ 8 10 12 13 14 16 18 20 21 24 26 27 29 30 33 36 ] - DeviceEIRP max_eirp_index = 1 [(gogoproto.customname) = "MaxEIRPIndex", (validate.rules).enum.defined_only = true]; + DeviceEIRP max_eirp_index = 1 [(validate.rules).enum.defined_only = true]; bool uplink_dwell_time = 2; bool downlink_dwell_time = 3; } @@ -512,17 +687,15 @@ message MACCommand { } message ADRParamSetupReq { // Exponent e that configures the ADR_ACK_LIMIT = 2^e messages. - ADRAckLimitExponent adr_ack_limit_exponent = 1 [(gogoproto.customname) = "ADRAckLimitExponent", (validate.rules).enum.defined_only = true]; + ADRAckLimitExponent adr_ack_limit_exponent = 1 [(validate.rules).enum.defined_only = true]; // Exponent e that configures the ADR_ACK_DELAY = 2^e messages. - ADRAckDelayExponent adr_ack_delay_exponent = 2 [(gogoproto.customname) = "ADRAckDelayExponent", (validate.rules).enum.defined_only = true]; + ADRAckDelayExponent adr_ack_delay_exponent = 2 [(validate.rules).enum.defined_only = true]; } message DeviceTimeAns { - google.protobuf.Timestamp time = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (validate.rules).timestamp.required = true]; + google.protobuf.Timestamp time = 7 [(validate.rules).timestamp.required = true]; } message ForceRejoinReq { - option (gogoproto.populate) = false; - - RejoinType rejoin_type = 1 [(validate.rules).enum.defined_only = true]; + RejoinRequestType rejoin_type = 1 [(validate.rules).enum.defined_only = true]; DataRateIndex data_rate_index = 2 [(validate.rules).enum.defined_only = true]; uint32 max_retries = 3 [(validate.rules).uint32.lte = 7]; // Exponent e that configures the rejoin period = 32 * 2^e + rand(0,32) seconds. @@ -541,8 +714,6 @@ message MACCommand { PingSlotPeriod period = 1 [(validate.rules).enum.defined_only = true]; } message PingSlotChannelReq { - option (gogoproto.populate) = false; - uint64 frequency = 1 [(validate.rules).uint64 = { lte: 0, gte: 100000 }]; // Ping slot channel frequency (Hz). DataRateIndex data_rate_index = 2 [(validate.rules).enum.defined_only = true]; } @@ -551,14 +722,10 @@ message MACCommand { bool data_rate_index_ack = 2; } message BeaconTimingAns { - option (gogoproto.populate) = false; - uint32 delay = 1 [(validate.rules).uint32.lte = 65535]; // (uint16) See LoRaWAN specification. uint32 channel_index = 2 [(validate.rules).uint32.lte = 255]; } message BeaconFreqReq { - option (gogoproto.populate) = false; - uint64 frequency = 1 [(validate.rules).uint64 = { lte: 0, gte: 100000 }]; // Frequency of the Class B beacons (Hz). } message BeaconFreqAns { @@ -572,8 +739,12 @@ message MACCommand { } } +message MACCommands { + repeated MACCommand commands = 1; +} + enum AggregatedDutyCycle { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "DUTY_CYCLE" }; DUTY_CYCLE_1 = 0; // 100%. DUTY_CYCLE_2 = 1; // 50%. @@ -594,7 +765,7 @@ enum AggregatedDutyCycle { } enum PingSlotPeriod { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "PING_EVERY" }; PING_EVERY_1S = 0; // Every second. PING_EVERY_2S = 1; // Every 2 seconds. @@ -607,7 +778,7 @@ enum PingSlotPeriod { } enum RejoinCountExponent { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "REJOIN_COUNT" }; REJOIN_COUNT_16 = 0; REJOIN_COUNT_32 = 1; @@ -628,7 +799,7 @@ enum RejoinCountExponent { } enum RejoinTimeExponent { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "REJOIN_TIME" }; REJOIN_TIME_0 = 0; // Every ~17.1 minutes. REJOIN_TIME_1 = 1; // Every ~34.1 minutes. @@ -649,7 +820,7 @@ enum RejoinTimeExponent { } enum RejoinPeriodExponent { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "REJOIN_PERIOD" }; REJOIN_PERIOD_0 = 0; // Every 32 to 64 seconds. REJOIN_PERIOD_1 = 1; // Every 64 to 96 seconds. @@ -662,7 +833,7 @@ enum RejoinPeriodExponent { } enum DeviceEIRP { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "DEVICE_EIRP" }; DEVICE_EIRP_8 = 0; // 8 dBm. DEVICE_EIRP_10 = 1; // 10 dBm. @@ -683,7 +854,7 @@ enum DeviceEIRP { } enum ADRAckLimitExponent { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "ADR_ACK_LIMIT" }; ADR_ACK_LIMIT_1 = 0; ADR_ACK_LIMIT_2 = 1; @@ -704,7 +875,7 @@ enum ADRAckLimitExponent { } enum ADRAckDelayExponent { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "ADR_ACK_DELAY" }; ADR_ACK_DELAY_1 = 0; ADR_ACK_DELAY_2 = 1; @@ -725,8 +896,8 @@ enum ADRAckDelayExponent { } enum RxDelay { - option (gogoproto.goproto_enum_prefix) = false; - option (gogoproto.enum_stringer) = false; + option (thethings.json.enum) = { marshal_as_number: true, prefix: "RX_DELAY" }; + option (thethings.flags.enum) = { alias_map: "go.thethings.network/lorawan-stack/v3/pkg/ttnpb.RxDelay_customvalue" }; RX_DELAY_0 = 0; // 1 second. RX_DELAY_1 = 1; // 1 second. @@ -747,7 +918,7 @@ enum RxDelay { } enum Minor { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "MINOR" }; MINOR_RFU_0 = 0; MINOR_1 = 1; @@ -767,21 +938,57 @@ enum Minor { MINOR_RFU_15 = 15; } +message FrequencyValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + uint64 value = 1 [(validate.rules).uint64.gte = 100000]; +} + +message ZeroableFrequencyValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + uint64 value = 1 [(validate.rules).uint64 = {lte: 0, gte: 100000}]; +} + +message DataRateOffsetValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + DataRateOffset value = 1 [(validate.rules).enum.defined_only = true]; +} message DataRateIndexValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; DataRateIndex value = 1 [(validate.rules).enum.defined_only = true]; } message PingSlotPeriodValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; PingSlotPeriod value = 1 [(validate.rules).enum.defined_only = true]; } message AggregatedDutyCycleValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; AggregatedDutyCycle value = 1 [(validate.rules).enum.defined_only = true]; } message RxDelayValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; RxDelay value = 1 [(validate.rules).enum.defined_only = true]; } message ADRAckLimitExponentValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + ADRAckLimitExponent value = 1 [(validate.rules).enum.defined_only = true]; } message ADRAckDelayExponentValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + ADRAckDelayExponent value = 1 [(validate.rules).enum.defined_only = true]; } +message DeviceEIRPValue { + option (thethings.flags.message) = { select: true, set: true, wrapper: true }; + option (thethings.json.message) = { wrapper: true }; + DeviceEIRP value = 1 [(validate.rules).enum.defined_only = true]; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/message_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/message_services.proto deleted file mode 100644 index 3d75a8085..000000000 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/message_services.proto +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2019 The Things Network Foundation, The Things Industries B.V. -// -// 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. - -syntax = "proto3"; - -import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; -import "lorawan-stack/api/end_device.proto"; -import "lorawan-stack/api/identifiers.proto"; -import "lorawan-stack/api/messages.proto"; - -package ttn.lorawan.v3; - -option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; - -message ProcessUplinkMessageRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - EndDeviceVersionIdentifiers end_device_version_ids = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "EndDeviceVersionIDs", (validate.rules).message.required = true]; - ApplicationUplink message = 3 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - string parameter = 4; -} - -message ProcessDownlinkMessageRequest { - EndDeviceIdentifiers ids = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true, (validate.rules).message.required = true]; - EndDeviceVersionIdentifiers end_device_version_ids = 2 [(gogoproto.nullable) = false, (gogoproto.customname) = "EndDeviceVersionIDs", (validate.rules).message.required = true]; - ApplicationDownlink message = 3 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; - string parameter = 4; -} - -// The UplinkMessageProcessor service processes uplink messages. -service UplinkMessageProcessor { - rpc Process(ProcessUplinkMessageRequest) returns (ApplicationUplink); -} - -// The DownlinkMessageProcessor service processes downlink messages. -service DownlinkMessageProcessor { - rpc Process(ProcessDownlinkMessageRequest) returns (ApplicationDownlink); -} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/messages.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/messages.proto index 770888c24..515908d10 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/messages.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/messages.proto @@ -16,21 +16,27 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; import "lorawan-stack/api/error.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/keys.proto"; import "lorawan-stack/api/lorawan.proto"; import "lorawan-stack/api/metadata.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // Uplink message from the end device to the network message UplinkMessage { - option (gogoproto.populate) = false; // Mapping from UDP message (other fields can be set in "advanced"): // @@ -73,21 +79,24 @@ message UplinkMessage { bytes raw_payload = 1; Message payload = 2; - TxSettings settings = 4 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + reserved 3; // end_device_ids (obsolete). + TxSettings settings = 4 [(validate.rules).message.required = true]; repeated RxMetadata rx_metadata = 5; // Server time when a component received the message. // The Gateway Server and Network Server set this value to their local server time of reception. - google.protobuf.Timestamp received_at = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - repeated string correlation_ids = 7 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + google.protobuf.Timestamp received_at = 6; + repeated string correlation_ids = 7 [(validate.rules).repeated.items.string.max_len = 100]; reserved 8; // gateway_channel_index (obsolete). // Index of the device channel that received the message. // Set by Network Server. uint32 device_channel_index = 9 [(validate.rules).uint32 = {lte: 255}]; + + // Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the RawPayload size and the transmission settings. + google.protobuf.Duration consumed_airtime = 10; } // Downlink message from the network to the end device message DownlinkMessage { - option (gogoproto.populate) = false; // Mapping from UDP message: // @@ -130,20 +139,26 @@ message DownlinkMessage { bytes raw_payload = 1; Message payload = 2; - EndDeviceIdentifiers end_device_ids = 3 [(gogoproto.customname) = "EndDeviceIDs"]; + EndDeviceIdentifiers end_device_ids = 3; oneof settings { option (validate.required) = true; TxRequest request = 4; TxSettings scheduled = 5; } - repeated string correlation_ids = 6 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + repeated string correlation_ids = 6 [(validate.rules).repeated.items.string.max_len = 100]; + bytes session_key_id = 7 [(validate.rules).bytes.max_len = 2048]; } message TxAcknowledgment { - repeated string correlation_ids = 1 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + // Correlation IDs for the downlink message. + // Set automatically by the UDP and LBS frontends. + // For gRPC and the MQTT v3 frontends, the correlation IDs must match the ones of the downlink message the Tx acknowledgment message refers to. + repeated string correlation_ids = 1 [(validate.rules).repeated.items.string.max_len = 100]; enum Result { + option (thethings.json.enum) = { marshal_as_string: true }; + SUCCESS = 0; UNKNOWN_ERROR = 1; TOO_LATE = 2; @@ -155,35 +170,58 @@ message TxAcknowledgment { GPS_UNLOCKED = 8; } Result result = 2 [(validate.rules).enum.defined_only = true]; + + // The acknowledged downlink message. Set by the Gateway Server. + DownlinkMessage downlink_message = 3; +} + +message GatewayTxAcknowledgment { + GatewayIdentifiers gateway_ids = 1; + TxAcknowledgment tx_ack = 2; } message GatewayUplinkMessage { - UplinkMessage message = 1 [(gogoproto.embed) = true, (validate.rules).message.required = true]; + UplinkMessage message = 1 [(validate.rules).message.required = true]; // LoRaWAN band ID of the gateway. - string band_id = 2 [(gogoproto.customname) = "BandID"]; + string band_id = 2; } message ApplicationUplink { + option (thethings.flags.message) = { select: true, set: false }; // Join Server issued identifier for the session keys used by this uplink. - bytes session_key_id = 1 [(gogoproto.customname) = "SessionKeyID", (validate.rules).bytes.max_len = 2048]; - uint32 f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255, not_in: [224]}]; + bytes session_key_id = 1 [(validate.rules).bytes.max_len = 2048]; + // LoRaWAN FPort of the uplink message. + uint32 f_port = 2 [(validate.rules).uint32 = {lte: 255, not_in: [224]}]; + // LoRaWAN FCntUp of the uplink message. uint32 f_cnt = 3; // The frame payload of the uplink message. // The payload is still encrypted if the skip_payload_crypto field of the EndDevice // is true, which is indicated by the presence of the app_s_key field. - bytes frm_payload = 4 [(gogoproto.customname) = "FRMPayload"]; + bytes frm_payload = 4; + // The decoded frame payload of the uplink message. + // This field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters). google.protobuf.Struct decoded_payload = 5; + // Warnings generated by the message processor while decoding the frm_payload. + repeated string decoded_payload_warnings = 12; + + // The normalized frame payload of the uplink message. + // This field is set by the message processor that is configured for the end device (see formatters) or application (see default_formatters). + // If the message processor is a custom script, there is no uplink normalizer script and the decoded output is valid + // normalized payload, this field contains the decoded payload. + repeated google.protobuf.Struct normalized_payload = 17; + // Warnings generated by the message processor while normalizing the decoded payload. + repeated string normalized_payload_warnings = 18; // A list of metadata for each antenna of each gateway that received this message. - repeated RxMetadata rx_metadata = 6 [(validate.rules).repeated.min_items = 1]; + repeated RxMetadata rx_metadata = 6; - // Settings for the transmission. - TxSettings settings = 7 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + // Transmission settings used by the end device. + TxSettings settings = 7 [(validate.rules).message.required = true]; // Server time when the Network Server received the message. - google.protobuf.Timestamp received_at = 8 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp received_at = 8; // The AppSKey of the current session. // This field is only present if the skip_payload_crypto field of the EndDevice @@ -196,19 +234,87 @@ message ApplicationUplink { // Can be used with app_s_key to encrypt downlink payloads. uint32 last_a_f_cnt_down = 10; + // Indicates whether the end device used confirmed data uplink. bool confirmed = 11; - // next: 12 + + // Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings. + google.protobuf.Duration consumed_airtime = 13; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 14; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 15; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 16; + + // next: 19 +} + +message ApplicationUplinkNormalized { + option (thethings.flags.message) = { select: true, set: false }; + // Join Server issued identifier for the session keys used by this uplink. + bytes session_key_id = 1 [(validate.rules).bytes.max_len = 2048]; + // LoRaWAN FPort of the uplink message. + uint32 f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255, not_in: [224]}]; + // LoRaWAN FCntUp of the uplink message. + uint32 f_cnt = 3; + + // The frame payload of the uplink message. + // This field is always decrypted with AppSKey. + bytes frm_payload = 4; + + // The normalized frame payload of the uplink message. + // This field is set for each item in normalized_payload in the corresponding ApplicationUplink message. + google.protobuf.Struct normalized_payload = 5 [(validate.rules).message.required = true]; + // This field is set to normalized_payload_warnings in the corresponding ApplicationUplink message. + repeated string normalized_payload_warnings = 6; + + // A list of metadata for each antenna of each gateway that received this message. + repeated RxMetadata rx_metadata = 7; + + // Transmission settings used by the end device. + TxSettings settings = 8 [(validate.rules).message.required = true]; + + // Server time when the Network Server received the message. + google.protobuf.Timestamp received_at = 9 [(validate.rules).timestamp.required = true]; + + // Indicates whether the end device used confirmed data uplink. + bool confirmed = 10; + + // Consumed airtime for the transmission of the uplink message. Calculated by Network Server using the raw payload size and the transmission settings. + google.protobuf.Duration consumed_airtime = 11; + + // End device location metadata, set by the Application Server while handling the message. + map locations = 12; + + // End device version identifiers, set by the Application Server while handling the message. + EndDeviceVersionIdentifiers version_ids = 13; + + // Network identifiers, set by the Network Server that handles the message. + NetworkIdentifiers network_ids = 14; + + // next: 15 } message ApplicationLocation { + option (thethings.flags.message) = { select: true, set: false }; string service = 1; - Location location = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - map attributes = 3 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + Location location = 2 [(validate.rules).message.required = true]; + map attributes = 3 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; } message ApplicationJoinAccept { + option (thethings.flags.message) = { select: true, set: false }; // Join Server issued identifier for the session keys negotiated in this join. - bytes session_key_id = 1 [(gogoproto.customname) = "SessionKeyID", (validate.rules).bytes.max_len = 2048]; + bytes session_key_id = 1 [(validate.rules).bytes.max_len = 2048]; // Encrypted Application Session Key (if Join Server sent it to Network Server). KeyEnvelope app_s_key = 2; // Downlink messages in the queue that got invalidated because of the session change. @@ -217,35 +323,55 @@ message ApplicationJoinAccept { // rejoin-request. bool pending_session = 4; // Server time when the Network Server received the message. - google.protobuf.Timestamp received_at = 8 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + google.protobuf.Timestamp received_at = 8 [(validate.rules).timestamp.required = true]; } message ApplicationDownlink { - option (gogoproto.populate) = false; - + option (thethings.flags.message) = { select: true, set: true }; // Join Server issued identifier for the session keys used by this downlink. - bytes session_key_id = 1 [(gogoproto.customname) = "SessionKeyID", (validate.rules).bytes.max_len = 2048]; - uint32 f_port = 2 [(validate.rules).uint32 = {gte: 1, lte: 255, not_in: [224]}]; + bytes session_key_id = 1 [ + (validate.rules).bytes.max_len = 2048, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; + uint32 f_port = 2 [(validate.rules).uint32 = {lte: 255, not_in: [224]}]; uint32 f_cnt = 3; // The frame payload of the downlink message. // The payload is encrypted if the skip_payload_crypto field of the EndDevice // is true. - bytes frm_payload = 4 [(gogoproto.customname) = "FRMPayload"]; + bytes frm_payload = 4 [ + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; + + // The decoded frame payload of the downlink message. + // When scheduling downlink with a message processor configured for the end device (see formatters) or application (see default_formatters), + // this fields acts as input for the downlink encoder, and the output is set to frm_payload. + // When reading downlink (listing the queue, downlink message events, etc), this fields acts as output of the downlink decoder, and the input is frm_payload. google.protobuf.Struct decoded_payload = 5; + // Warnings generated by the message processor while encoding frm_payload (scheduling downlink) or decoding the frm_payload (reading downlink). + repeated string decoded_payload_warnings = 10; + bool confirmed = 6; message ClassBC { - // Possible gateway identifiers and antenna index to use for this downlink message. + option (thethings.flags.message) = { select: true, set: true }; + // Possible gateway identifiers, antenna index, and group index to use for this downlink message. // The Network Server selects one of these gateways for downlink, based on connectivity, signal quality, channel utilization and an available slot. // If none of the gateways can be selected, the downlink message fails. // If empty, a gateway and antenna is selected automatically from the gateways seen in recent uplinks. - repeated GatewayAntennaIdentifiers gateways = 7 [(gogoproto.nullable) = false]; + // If group index is set, gateways will be grouped by the index for the Network Server to select one gateway per group. + repeated ClassBCGatewayIdentifiers gateways = 7; // Absolute time when the downlink message should be transmitted. // This requires the gateway to have GPS time synchronization. // If the time is in the past or if there is a scheduling conflict, the downlink message fails. // If null, the time is selected based on slot availability. This is recommended in class B mode. - google.protobuf.Timestamp absolute_time = 8 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp absolute_time = 8; } // Optional gateway and timing information for class B and C. // If set, this downlink message will only be transmitted as class B or C downlink. @@ -254,7 +380,9 @@ message ApplicationDownlink { // Priority for scheduling the downlink message. TxSchedulePriority priority = 8 [(validate.rules).enum.defined_only = true]; - repeated string correlation_ids = 9 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + repeated string correlation_ids = 9 [(validate.rules).repeated.items.string.max_len = 100]; + + // next: 11 } message ApplicationDownlinks { @@ -262,30 +390,65 @@ message ApplicationDownlinks { } message ApplicationDownlinkFailed { - ApplicationDownlink downlink = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ErrorDetails error = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: true, set: false }; + ApplicationDownlink downlink = 1 [(validate.rules).message.required = true]; + ErrorDetails error = 2 [(validate.rules).message.required = true]; } message ApplicationInvalidatedDownlinks { - repeated ApplicationDownlink downlinks = 1 [(validate.rules).repeated.min_items = 1]; + option (thethings.flags.message) = { select: true, set: false }; + repeated ApplicationDownlink downlinks = 1; uint32 last_f_cnt_down = 2; + bytes session_key_id = 3 [(validate.rules).bytes.max_len = 2048]; +} + +message DownlinkQueueOperationErrorDetails { + bytes dev_addr = 1 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + bytes session_key_id = 2 [(validate.rules).bytes.max_len = 2048]; + uint32 min_f_cnt_down = 3; + + bytes pending_dev_addr = 4 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; + bytes pending_session_key_id = 5 [(validate.rules).bytes.max_len = 2048]; + uint32 pending_min_f_cnt_down = 6; } message ApplicationServiceData { + option (thethings.flags.message) = { select: true, set: false }; string service = 1; google.protobuf.Struct data = 2; } +// Application uplink message. message ApplicationUp { - EndDeviceIdentifiers end_device_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - repeated string correlation_ids = 2 [(gogoproto.customname) = "CorrelationIDs", (validate.rules).repeated.items.string.max_len = 100]; + option (thethings.flags.message) = { select: true, set: false }; + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + repeated string correlation_ids = 2 [(validate.rules).repeated.items.string.max_len = 100]; // Server time when the Application Server received the message. - google.protobuf.Timestamp received_at = 12 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp received_at = 12; oneof up { option (validate.required) = true; ApplicationUplink uplink_message = 3; + ApplicationUplinkNormalized uplink_normalized = 15; ApplicationJoinAccept join_accept = 4; ApplicationDownlink downlink_ack = 5; ApplicationDownlink downlink_nack = 6; @@ -299,9 +462,13 @@ message ApplicationUp { // Signals if the message is coming from the Network Server or is simulated. bool simulated = 14; + + // next: 17 } enum PayloadFormatter { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "FORMATTER" }; + // No payload formatter to work with raw payload only. FORMATTER_NONE = 0; // Use payload formatter for the end device type from a repository. @@ -316,17 +483,18 @@ enum PayloadFormatter { } message MessagePayloadFormatters { + option (thethings.flags.message) = { select: true, set: true }; // Payload formatter for uplink messages, must be set together with its parameter. PayloadFormatter up_formatter = 1 [(validate.rules).enum.defined_only = true]; - // Parameter for the up_formatter, must be set together. - string up_formatter_parameter = 2; + // Parameter for the up_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration. + string up_formatter_parameter = 2 [(validate.rules).string.max_len = 40960]; // Payload formatter for downlink messages, must be set together with its parameter. PayloadFormatter down_formatter = 3 [(validate.rules).enum.defined_only = true]; - // Parameter for the down_formatter, must be set together. - string down_formatter_parameter = 4; + // Parameter for the down_formatter, must be set together. The API enforces a maximum length of 16KB, but the size may be restricted further by deployment configuration. + string down_formatter_parameter = 4 [(validate.rules).string.max_len = 40960]; } message DownlinkQueueRequest { - EndDeviceIdentifiers end_device_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; - repeated ApplicationDownlink downlinks = 2; + EndDeviceIdentifiers end_device_ids = 1 [(validate.rules).message.required = true]; + repeated ApplicationDownlink downlinks = 2 [(validate.rules).repeated.max_items = 100000]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/metadata.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/metadata.proto index 78b93a480..6df4cc4e1 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/metadata.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/metadata.proto @@ -14,46 +14,51 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/wrappers.proto"; import "lorawan-stack/api/enums.proto"; import "lorawan-stack/api/identifiers.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // Contains metadata for a received message. Each antenna that receives // a message corresponds to one RxMetadata. message RxMetadata { - option (gogoproto.populate) = false; - - GatewayIdentifiers gateway_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + GatewayIdentifiers gateway_ids = 1 [(validate.rules).message.required = true]; PacketBrokerMetadata packet_broker = 18; uint32 antenna_index = 2; - google.protobuf.Timestamp time = 3 [(gogoproto.stdtime) = true]; + // Timestamp at the end of the transmission, provided by the gateway. The accuracy is undefined. + google.protobuf.Timestamp time = 3; // Gateway concentrator timestamp when the Rx finished (microseconds). uint32 timestamp = 4; // Gateway's internal fine timestamp when the Rx finished (nanoseconds). uint64 fine_timestamp = 5; // Encrypted gateway's internal fine timestamp when the Rx finished (nanoseconds). bytes encrypted_fine_timestamp = 6; - string encrypted_fine_timestamp_key_id = 7 [(gogoproto.customname) = "EncryptedFineTimestampKeyID"]; + string encrypted_fine_timestamp_key_id = 7; // Received signal strength indicator (dBm). // This value equals `channel_rssi`. - float rssi = 8 [(gogoproto.customname) = "RSSI"]; + float rssi = 8; // Received signal strength indicator of the signal (dBm). - google.protobuf.FloatValue signal_rssi = 16 [(gogoproto.customname) = "SignalRSSI"]; + google.protobuf.FloatValue signal_rssi = 16; // Received signal strength indicator of the channel (dBm). - float channel_rssi = 9 [(gogoproto.customname) = "ChannelRSSI"]; + float channel_rssi = 9; // Standard deviation of the RSSI during preamble. - float rssi_standard_deviation = 10 [(gogoproto.customname) = "RSSIStandardDeviation"]; + float rssi_standard_deviation = 10; // Signal-to-noise ratio (dB). - float snr = 11 [(gogoproto.customname) = "SNR"]; + float snr = 11; // Frequency offset (Hz). int64 frequency_offset = 12; // Antenna location; injected by the Gateway Server. @@ -64,15 +69,25 @@ message RxMetadata { bytes uplink_token = 15; // Index of the gateway channel that received the message. uint32 channel_index = 17 [(validate.rules).uint32 = {lte: 255}]; + // Hopping width; a number describing the number of steps of the LR-FHSS grid. + uint32 hopping_width = 19; + // Frequency drift in Hz between start and end of an LR-FHSS packet (signed). + int32 frequency_drift = 20; + // Timestamp at the end of the transmission, provided by the gateway. + // Guaranteed to be based on a GPS PPS signal, with an accuracy of 1 millisecond. + google.protobuf.Timestamp gps_time = 21; + // Timestamp at which the Gateway Server has received the message. + google.protobuf.Timestamp received_at = 22; // Advanced metadata fields // - can be used for advanced information or experimental features that are not yet formally defined in the API // - field names are written in snake_case google.protobuf.Struct advanced = 99; - // next: 19 + // next: 23 } message Location { + option (thethings.flags.message) = { select: true, set: true }; // The North–South position (degrees; -90 to +90), where 0 is the equator, North pole is positive, South pole is negative. double latitude = 1 [(validate.rules).double = {gte: -90, lte: 90}]; // The East-West position (degrees; -180 to +180), where 0 is the Prime Meridian (Greenwich), East is positive , West is negative. @@ -82,11 +97,15 @@ message Location { // The accuracy of the location (meters). int32 accuracy = 4; // Source of the location information. - LocationSource source = 5 [(validate.rules).enum.defined_only = true]; + LocationSource source = 5 [ + (validate.rules).enum.defined_only = true, + (thethings.flags.field) = { set: false } + ]; } enum LocationSource { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "SOURCE" }; + // The source of the location is not known or not set. SOURCE_UNKNOWN = 0; // The location is determined by GPS. @@ -110,25 +129,58 @@ enum LocationSource { message PacketBrokerMetadata { // Message identifier generated by Packet Broker Router. - string message_id = 1 [(gogoproto.customname) = "MessageID"]; + string message_id = 1; // LoRa Alliance NetID of the Packet Broker Forwarder Member. - bytes forwarder_net_id = 2 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID", (gogoproto.customname) = "ForwarderNetID", (gogoproto.nullable) = false]; + bytes forwarder_net_id = 2 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; // Tenant ID managed by the Packet Broker Forwarder Member. - string forwarder_tenant_id = 3 [(gogoproto.customname) = "ForwarderTenantID"]; - // Forwarder identifier issued by the Packet Broker Forwarder Member. - string forwarder_id = 4 [(gogoproto.customname) = "ForwarderID"]; + string forwarder_tenant_id = 3; + // Forwarder Cluster ID of the Packet Broker Forwarder. + string forwarder_cluster_id = 4; + // Forwarder gateway EUI. + bytes forwarder_gateway_eui = 9 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + // Forwarder gateway ID. + google.protobuf.StringValue forwarder_gateway_id = 10; // LoRa Alliance NetID of the Packet Broker Home Network Member. - bytes home_network_net_id = 5 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.NetID", (gogoproto.customname) = "HomeNetworkNetID", (gogoproto.nullable) = false]; + bytes home_network_net_id = 5 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; // Tenant ID managed by the Packet Broker Home Network Member. // This value is empty if it cannot be determined by the Packet Broker Router. - string home_network_tenant_id = 6 [(gogoproto.customname) = "HomeNetworkTenantID"]; + string home_network_tenant_id = 6; + // Home Network Cluster ID of the Packet Broker Home Network. + string home_network_cluster_id = 8; // Hops that the message passed. Each Packet Broker Router service appends an entry. repeated PacketBrokerRouteHop hops = 7; } message PacketBrokerRouteHop { // Time when the service received the message. - google.protobuf.Timestamp received_at = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp received_at = 1; // Sender of the message, typically the authorized client identifier. string sender_name = 2; // Sender IP address or host name. diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/mqtt.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/mqtt.proto index 6e8a28dc5..6ef10f6d5 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/mqtt.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/mqtt.proto @@ -21,12 +21,15 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // The connection information of an MQTT frontend. message MQTTConnectionInfo { // The public listen address of the frontend. string public_address = 1 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The public listen address of the TLS frontend. - string public_tls_address = 2 [(gogoproto.customname) = "PublicTLSAddress", (validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; + string public_tls_address = 2 [(validate.rules).string.pattern = "^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*(?:[A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])(?::[0-9]{1,5})?$|^$"]; // The username to be used for authentication. string username = 3; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/networkserver.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/networkserver.proto index 5bfce86b5..cdceb6158 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/networkserver.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/networkserver.proto @@ -14,21 +14,77 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/end_device.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/messages.proto"; +import "lorawan-stack/api/lorawan.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// Response of GenerateDevAddr. message GenerateDevAddrResponse { - bytes dev_addr = 1 [(gogoproto.customtype) = "go.thethings.network/lorawan-stack/v3/pkg/types.DevAddr"]; + bytes dev_addr = 1 [ + (validate.rules).bytes = { len: 4, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal4Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"2600ABCD\"" + } + ]; +} + +// Request of GetDefaultMACSettings. +message GetDefaultMACSettingsRequest { + option (thethings.flags.message) = { select: false, set: true }; + string frequency_plan_id = 1 [(validate.rules).string.max_len = 64]; + PHYVersion lorawan_phy_version = 2 [(validate.rules).enum.defined_only = true]; +} + +message GetNetIDResponse { + bytes net_id = 1 [ + (validate.rules).bytes = { len: 3, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal3Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"000013\"" + } + ]; +} + +message GetDeviceAdressPrefixesResponse { + repeated bytes dev_addr_prefixes = 1 [ + (validate.rules).repeated = { + items: { + bytes: { len: 5 } + } + }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalDevAddrPrefixSlice", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.UnmarshalDevAddrPrefixSlice" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + example: "[\"2600AB00/24\"]" // NOTE: openapiv2 generator does not support customizing array items. + } + ]; } +// The Ns service manages the Network Server. service Ns { // GenerateDevAddr requests a device address assignment from the Network Server. rpc GenerateDevAddr(google.protobuf.Empty) returns (GenerateDevAddrResponse) { @@ -36,19 +92,45 @@ service Ns { get: "/ns/dev_addr" }; }; + // GetDefaultMACSettings retrieves the default MAC settings for a frequency plan. + rpc GetDefaultMACSettings(GetDefaultMACSettingsRequest) returns (MACSettings) { + option (google.api.http) = { + get: "/ns/default_mac_settings/{frequency_plan_id}/{lorawan_phy_version}" + }; + }; + + rpc GetNetID(google.protobuf.Empty) returns (GetNetIDResponse) { + option (google.api.http) = { + get: "/ns/net_id" + }; + }; + + rpc GetDeviceAddressPrefixes(google.protobuf.Empty) returns (GetDeviceAdressPrefixesResponse) { + option (google.api.http) = { + get: "/ns/dev_addr_prefixes" + }; + }; } // The AsNs service connects an Application Server to a Network Server. service AsNs { - rpc LinkApplication(stream google.protobuf.Empty) returns (stream ApplicationUp); + // Replace the entire downlink queue with the specified messages. + // This can also be used to empty the queue by specifying no messages. + // Note that this will trigger an immediate downlink if a downlink slot is available. rpc DownlinkQueueReplace(DownlinkQueueRequest) returns (google.protobuf.Empty); + // Push downlink messages to the end of the downlink queue. + // Note that this will trigger an immediate downlink if a downlink slot is available. rpc DownlinkQueuePush(DownlinkQueueRequest) returns (google.protobuf.Empty); + // List the items currently in the downlink queue. rpc DownlinkQueueList(EndDeviceIdentifiers) returns (ApplicationDownlinks); } // The GsNs service connects a Gateway Server to a Network Server. service GsNs { + // Called by the Gateway Server when an uplink message arrives. rpc HandleUplink(UplinkMessage) returns (google.protobuf.Empty); + // Called by the Gateway Server when a Tx acknowledgment arrives. + rpc ReportTxAcknowledgment(GatewayTxAcknowledgment) returns (google.protobuf.Empty); } // The NsEndDeviceRegistry service allows clients to manage their end devices on the Network Server. @@ -73,6 +155,14 @@ service NsEndDeviceRegistry { }; }; + // ResetFactoryDefaults resets device state to factory defaults. + rpc ResetFactoryDefaults(ResetAndGetEndDeviceRequest) returns (EndDevice) { + option (google.api.http) = { + patch: "/ns/applications/{end_device_ids.application_ids.application_id}/devices/{end_device_ids.device_id}" + body: "*" + }; + }; + // Delete deletes the device that matches the given identifiers. // If there are multiple matches, an error will be returned. rpc Delete(EndDeviceIdentifiers) returns (google.protobuf.Empty) { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/notification_service.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/notification_service.proto new file mode 100644 index 000000000..6497504ad --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/notification_service.proto @@ -0,0 +1,208 @@ +// Copyright © 2022 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "lorawan-stack/api/identifiers.proto"; +import "lorawan-stack/api/enums.proto"; + +package ttn.lorawan.v3; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +message Notification { + // The immutable ID of the notification. Generated by the server. + string id = 1; + + // The time when the notification was triggered. + google.protobuf.Timestamp created_at = 2; + + // The entity this notification is about. + EntityIdentifiers entity_ids = 3; + + // The type of this notification. + string notification_type = 4; + + // The data related to the notification. + google.protobuf.Any data = 5; + + // If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification. + UserIdentifiers sender_ids = 6; + + // If the notification was triggered by an event pipeline, this contains the identifiers of that pipeline. + // EventPipelineIdentifiers event_pipeline_ids = 7; + reserved "event_pipeline_ids"; reserved 7; + + // Relation of the notification receiver to the entity. + repeated NotificationReceiver receivers = 8; + + // Whether an email was sent for the notification. + bool email = 9; + + // The status of the notification. + NotificationStatus status = 10; + + // The time when the notification status was updated. + google.protobuf.Timestamp status_updated_at = 11; +} + +enum NotificationReceiver { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "NOTIFICATION_RECEIVER" }; + + NOTIFICATION_RECEIVER_UNKNOWN = 0; + + // Notification is received by collaborators of the entity. + // If the collaborator is an organization, the notification is received by organization members. + NOTIFICATION_RECEIVER_COLLABORATOR = 1; + + // Notification is received by administrative contact of the entity. + // If this is an organization, the notification is received by organization members. + NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT = 3; + + // Notification is received by technical contact of the entity. + // If this is an organization, the notification is received by organization members. + NOTIFICATION_RECEIVER_TECHNICAL_CONTACT = 4; +} + +enum NotificationStatus { + option (thethings.json.enum) = { marshal_as_string: true, prefix: "NOTIFICATION_STATUS" }; + + NOTIFICATION_STATUS_UNSEEN = 0; + NOTIFICATION_STATUS_SEEN = 1; + NOTIFICATION_STATUS_ARCHIVED = 2; +} + +message CreateNotificationRequest { + // The entity this notification is about. + EntityIdentifiers entity_ids = 1 [(validate.rules).message.required = true]; + + // The type of this notification. + string notification_type = 2 [ + (validate.rules).string = { + min_len: 1, + max_len: 100, + } + ]; + + // The data related to the notification. + google.protobuf.Any data = 3; + + // If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification. + UserIdentifiers sender_ids = 4; + + // Receivers of the notification. + repeated NotificationReceiver receivers = 5 [ + (validate.rules).repeated = { + min_items: 1, + unique: true, + items: { enum: { defined_only: true } } + } + ]; + + // Whether an email should be sent for the notification. + bool email = 6; +} + +message CreateNotificationResponse { + string id = 1; +} + +message ListNotificationsRequest { + option (thethings.flags.message) = { select: false, set: true }; + + // The IDs of the receiving user. + UserIdentifiers receiver_ids = 1 [(validate.rules).message.required = true]; + + // Select notifications with these statuses. + // An empty list is interpreted as "all". + repeated NotificationStatus status = 2 [ + (validate.rules).repeated = { + unique: true, + items: { enum: { defined_only: true } } + } + ]; + + // Limit the number of results per page. + uint32 limit = 3 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 4; +} + +message ListNotificationsResponse { + repeated Notification notifications = 1; +} + +message UpdateNotificationStatusRequest { + option (thethings.flags.message) = { select: false, set: true }; + + // The IDs of the receiving user. + UserIdentifiers receiver_ids = 1 [(validate.rules).message.required = true]; + + // The IDs of the notifications to update the status of. + repeated string ids = 2 [ + (validate.rules).repeated = { + min_items: 1, + max_items: 1000, + unique: true, + items: { string: { len: 36 } } + } + ]; + + // The status to set on the notifications. + NotificationStatus status = 3 [ + (validate.rules).enum = { + defined_only: true + } + ]; +} + +service NotificationService { + // Create a new notification. Can only be called by internal services using cluster auth. + rpc Create(CreateNotificationRequest) returns (CreateNotificationResponse); + + // List the notifications for a user or an organization. + // When called with user credentials and empty receiver_ids, this will list + // notifications for the current user and its organizations. + rpc List(ListNotificationsRequest) returns (ListNotificationsResponse) { + option (google.api.http) = { + additional_bindings { + get: "/users/{receiver_ids.user_id}/notifications" + } + }; + } + + // Batch-update multiple notifications to the same status. + rpc UpdateStatus(UpdateNotificationStatusRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + patch: "/users/{receiver_ids.user_id}/notifications" + body: "*" + }; + } +} + +message EntityStateChangedNotification { + State state = 1 [(validate.rules).enum.defined_only = true]; + string state_description = 2 [(validate.rules).string.max_len = 128]; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth.proto index ffad6cf05..57e824c11 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth.proto @@ -24,17 +24,20 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message OAuthClientAuthorizationIdentifiers { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; } message OAuthClientAuthorization { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; repeated Right rights = 3; - google.protobuf.Timestamp created_at = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp created_at = 4; + google.protobuf.Timestamp updated_at = 5; } message OAuthClientAuthorizations { @@ -42,7 +45,7 @@ message OAuthClientAuthorizations { } message ListOAuthClientAuthorizationsRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 2 [ @@ -55,33 +58,33 @@ message ListOAuthClientAuthorizationsRequest { } message OAuthAuthorizationCode { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string user_session_id = 9 [(gogoproto.customname) = "UserSessionID", (validate.rules).string.max_len = 64]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + string user_session_id = 9 [(validate.rules).string.max_len = 64]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; repeated Right rights = 3; string code = 4; - string redirect_uri = 5 [(gogoproto.customname) = "RedirectURI", (validate.rules).string.uri_ref = true]; + string redirect_uri = 5 [(validate.rules).string.uri_ref = true]; string state = 6; - google.protobuf.Timestamp created_at = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp expires_at = 8 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp created_at = 7; + google.protobuf.Timestamp expires_at = 8; } message OAuthAccessTokenIdentifiers { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string id = 3 [(gogoproto.customname) = "ID"]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; + string id = 3; } message OAuthAccessToken { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string user_session_id = 9 [(gogoproto.customname) = "UserSessionID", (validate.rules).string.max_len = 64]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string id = 3 [(gogoproto.customname) = "ID"]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + string user_session_id = 9 [(validate.rules).string.max_len = 64]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; + string id = 3; string access_token = 4; string refresh_token = 5; repeated Right rights = 6; - google.protobuf.Timestamp created_at = 7 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp expires_at = 8 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp created_at = 7; + google.protobuf.Timestamp expires_at = 8; } message OAuthAccessTokens { @@ -89,8 +92,8 @@ message OAuthAccessTokens { } message ListOAuthAccessTokensRequest { - UserIdentifiers user_ids = 1 [(gogoproto.customname) = "UserIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; - ClientIdentifiers client_ids = 2 [(gogoproto.customname) = "ClientIDs", (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + ClientIdentifiers client_ids = 2 [(validate.rules).message.required = true]; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth_services.proto index 779ebd814..6e63da8e7 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/oauth_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/oauth.proto"; @@ -22,22 +23,31 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The OAuthAuthorizationRegistry service, exposed by the Identity Server, +// is used to manage OAuth client authorizations for users. service OAuthAuthorizationRegistry { + // List OAuth clients that are authorized by the user. rpc List(ListOAuthClientAuthorizationsRequest) returns (OAuthClientAuthorizations) { option (google.api.http) = { get: "/users/{user_ids.user_id}/authorizations" }; } + // List OAuth access tokens issued to the OAuth client on behalf of the user. rpc ListTokens(ListOAuthAccessTokensRequest) returns (OAuthAccessTokens) { option (google.api.http) = { get: "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens" }; } + // Delete (de-authorize) an OAuth client for the user. rpc Delete(OAuthClientAuthorizationIdentifiers) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}" }; } + // Delete (invalidate) an OAuth access token. rpc DeleteToken(OAuthAccessTokenIdentifiers) returns (google.protobuf.Empty) { option (google.api.http) = { delete: "/users/{user_ids.user_id}/authorizations/{client_ids.client_id}/tokens/{id}" diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization.proto index 730ab65ef..599284fe2 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/protobuf/field_mask.proto"; @@ -26,19 +27,55 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message Organization { - OrganizationIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + // The identifiers of the organization. These are public and can be seen by any authenticated user in the network. + OrganizationIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + // When the organization was created. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the organization was last updated. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the organization was deleted. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp deleted_at = 8 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // The name of the organization. This information is public and can be seen by any authenticated user in the network. string name = 4 [(validate.rules).string.max_len = 50]; + // A description for the organization. string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this organization. Typically used for organizing organizations or for storing integration-specific data. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Contact information for this organization. Typically used to indicate who to contact with security/billing questions about the organization. - repeated ContactInfo contact_info = 7; + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; + + OrganizationOrUserIdentifiers administrative_contact = 9; + OrganizationOrUserIdentifiers technical_contact = 10; + + reserved 11; reserved "application_limit"; + reserved 12; reserved "client_limit"; + reserved 13; reserved "gateway_limit"; + + // next: 14 } message Organizations { @@ -46,20 +83,23 @@ message Organizations { } message GetOrganizationRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; // The names of the organization fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListOrganizationsRequest { + option (thethings.flags.message) = { select: false, set: true }; // By default we list all organizations the caller has rights on. // Set the user to instead list the organizations // where the user or organization is collaborator on. // NOTE: It is currently not possible to have organizations collaborating on // other organizations. - OrganizationOrUserIdentifiers collaborator = 1; + OrganizationOrUserIdentifiers collaborator = 1 [ + (thethings.flags.field) = { hidden: true } + ]; // The names of the organization fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 3 [ @@ -69,24 +109,33 @@ message ListOrganizationsRequest { uint32 limit = 4 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 5; + // Only return recently deleted organizations. + bool deleted = 6; } message CreateOrganizationRequest { - Organization organization = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Organization organization = 1 [(validate.rules).message.required = true]; // Collaborator to grant all rights on the newly created application. // NOTE: It is currently not possible to have organizations collaborating on // other organizations. - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message UpdateOrganizationRequest { - Organization organization = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + Organization organization = 1 [(validate.rules).message.required = true]; // The names of the organization fields that should be updated. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListOrganizationAPIKeysRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: false, set: true }; + + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; + // Order the results by this field path. + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "api_key_id", "-api_key_id", "name", "-name", "created_at", "-created_at", "expires_at", "-expires_at"] } + ]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. @@ -94,38 +143,52 @@ message ListOrganizationAPIKeysRequest { } message GetOrganizationAPIKeyRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; // Unique public identifier for the API key. - string key_id = 2 [(gogoproto.customname) = "KeyID"]; + string key_id = 2; } message CreateOrganizationAPIKeyRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; string name = 2 [(validate.rules).string.max_len = 50]; - repeated Right rights = 3 [(validate.rules).repeated.items.enum.defined_only = true]; + repeated Right rights = 3 [ + (validate.rules).repeated = { + min_items: 1, + unique: true, + items: { enum: { defined_only: true } } + } + ]; + google.protobuf.Timestamp expires_at = 4 [(validate.rules).timestamp.gt_now = true]; } message UpdateOrganizationAPIKeyRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - APIKey api_key = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; + APIKey api_key = 2 [(validate.rules).message.required = true]; + // The names of the api key fields that should be updated. + google.protobuf.FieldMask field_mask = 3; } message ListOrganizationCollaboratorsRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 3; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "id", "-id", "-rights", "rights"] } + ]; } message GetOrganizationCollaboratorRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; // NOTE: It is currently not possible to have organizations collaborating on // other organizations. - OrganizationOrUserIdentifiers collaborator = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers collaborator = 2 [(validate.rules).message.required = true]; } message SetOrganizationCollaboratorRequest { - OrganizationIdentifiers organization_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - Collaborator collaborator = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationIdentifiers organization_ids = 1 [(validate.rules).message.required = true]; + Collaborator collaborator = 2 [(validate.rules).message.required = true]; } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization_services.proto index 0c28119a3..cce48d0b5 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/organization_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/identifiers.proto"; @@ -24,6 +25,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The OrganizationRegistry service, exposed by the Identity Server, is used to manage +// organization registrations. service OrganizationRegistry { // Create a new organization. This also sets the given user as // first collaborator with all possible rights. @@ -71,8 +77,29 @@ service OrganizationRegistry { delete: "/organizations/{organization_id}" }; }; + + // Restore a recently deleted organization. + // + // Deployment configuration may specify if, and for how long after deletion, + // entities can be restored. + rpc Restore(OrganizationIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/organizations/{organization_id}/restore" + }; + }; + + // Purge the organization. This will release the organization ID for reuse. + // The user is responsible for clearing data from any (external) integrations + // that may store and expose data by user or organization ID. + rpc Purge(OrganizationIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/organizations/{organization_id}/purge" + }; + }; } +// The OrganizationAcces service, exposed by the Identity Server, is used to manage +// API keys and collaborators of organizations. service OrganizationAccess { // List the rights the caller has on this organization. rpc ListRights(OrganizationIdentifiers) returns (Rights) { diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/packetbrokeragent.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/packetbrokeragent.proto index 76d975817..7f8a178b9 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/packetbrokeragent.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/packetbrokeragent.proto @@ -1,4 +1,4 @@ -// Copyright © 2020 The Things Network Foundation, The Things Industries B.V. +// Copyright © 2021 The Things Network Foundation, The Things Industries B.V. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,20 +14,461 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; +import "lorawan-stack/api/contact_info.proto"; +import "lorawan-stack/api/gateway.proto"; +import "lorawan-stack/api/end_device.proto"; +import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/messages.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // The GsPba service connects a Gateway Server to a Packet Broker Agent. service GsPba { rpc PublishUplink(GatewayUplinkMessage) returns (google.protobuf.Empty); + + // Update the gateway, changing the fields specified by the field mask to the provided values. + // To mark a gateway as online, call this rpc setting online to true, include status_public in field_mask and + // keep calling this rpc before the returned online_ttl passes to keep the gateway online. + rpc UpdateGateway(UpdatePacketBrokerGatewayRequest) returns (UpdatePacketBrokerGatewayResponse); +} + +// Gateway respresentation for Packet Broker. +// This is a subset and superset of the Gateway message using the same data types and field tags to achieve initial wire compatibility. +// There is no (longer) wire compatibility needed; new fields may use any tag. +message PacketBrokerGateway { + message GatewayIdentifiers { + string gateway_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[_-]?[a-z0-9]){2,}$", max_len: 36}]; + bytes eui = 2 [ + (validate.rules).bytes = { len: 8, ignore_empty: true }, + (thethings.json.field) = { + marshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.MarshalHEXBytes", + unmarshaler_func: "go.thethings.network/lorawan-stack/v3/pkg/types.Unmarshal8Bytes" + }, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + type: STRING, format: "string", example: "\"70B3D57ED000ABCD\"" + } + ]; + } + GatewayIdentifiers ids = 1 [(validate.rules).message.required = true]; + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; + OrganizationOrUserIdentifiers administrative_contact = 10; + OrganizationOrUserIdentifiers technical_contact = 11; + repeated GatewayAntenna antennas = 13 [ + (validate.rules).repeated.max_items = 8 + ]; + bool status_public = 14; + bool location_public = 15; + repeated string frequency_plan_ids = 20 [ + (validate.rules).repeated = { + max_items: 8, + items: { string: { max_len: 64 } } + } + ]; + bool update_location_from_status = 21; + bool online = 28; + // Received packets rate (number of packets per hour). + // This field gets updated when a value is set. + google.protobuf.FloatValue rx_rate = 29; + // Transmitted packets rate (number of packets per hour). + // This field gets updated when a value is set. + google.protobuf.FloatValue tx_rate = 30; +} + +message UpdatePacketBrokerGatewayRequest { + PacketBrokerGateway gateway = 1 [(validate.rules).message.required = true]; + + // The names of the gateway fields that are considered for update. + // + // Online status is only updated if status_public is set. If status_public is set and false, the status will be reset. + // If status_public is set and true, the online status is taken from the online field. The return message contains + // the duration online_ttl for how long the gateway is considered online. + // + // Location is only updated if location_public is set. If location_public is set and false, the location will be reset. + // If location_public is set and true, the first antenna location will be used as gateway location. + google.protobuf.FieldMask field_mask = 5; +} + +message UpdatePacketBrokerGatewayResponse { + // Time to live of the online status. + google.protobuf.Duration online_ttl = 1; } // The NsPba service connects a Network Server to a Packet Broker Agent. service NsPba { - // PublishDownlink instructs the Packet Broker Agent to publish a downlink message to Packet Broker Router. + // PublishDownlink instructs the Packet Broker Agent to publish a downlink + // message to Packet Broker Router. rpc PublishDownlink(DownlinkMessage) returns (google.protobuf.Empty); } + +message PacketBrokerNetworkIdentifier { + // LoRa Alliance NetID. + uint32 net_id = 1; + // Tenant identifier if the registration leases DevAddr blocks from a NetID. + string tenant_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; +} + +message PacketBrokerDevAddrBlock { + DevAddrPrefix dev_addr_prefix = 1; + string home_network_cluster_id = 2; +} + +message PacketBrokerNetwork { + // Packet Broker network identifier. + PacketBrokerNetworkIdentifier id = 1; + // Name of the network. + string name = 2; + // DevAddr blocks that are assigned to this registration. + repeated PacketBrokerDevAddrBlock dev_addr_blocks = 3; + // Contact information. + // This field is deprecated. Use administrative_contact and technical_contact instead. + repeated ContactInfo contact_info = 4 [deprecated = true, (validate.rules).repeated.max_items = 10]; + ContactInfo administrative_contact = 6; + ContactInfo technical_contact = 7; + // Whether the network is listed so it can be viewed by other networks. + bool listed = 5; + + // next: 8 +} + +message PacketBrokerNetworks { + repeated PacketBrokerNetwork networks = 1; +} + +message PacketBrokerInfo { + // The current registration, unset if there isn't a registration. + PacketBrokerNetwork registration = 1; + // Whether the server is configured as Forwarder (with gateways). + bool forwarder_enabled = 2; + // Whether the server is configured as Home Network (with end devices). + bool home_network_enabled = 3; + // Whether the registration can be changed. + bool register_enabled = 4; +} + +message PacketBrokerRegisterRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Whether the network should be listed in Packet Broker. + // If unset, the value is taken from the registration settings. + google.protobuf.BoolValue listed = 1; +} + +message PacketBrokerRoutingPolicyUplink { + // Forward join-request messages. + bool join_request = 1; + // Forward uplink messages with FPort of 0. + bool mac_data = 2; + // Forward uplink messages with FPort between 1 and 255. + bool application_data = 3; + // Forward RSSI and SNR. + bool signal_quality = 4; + // Forward gateway location, RSSI, SNR and fine timestamp. + bool localization = 5; +} + +message PacketBrokerRoutingPolicyDownlink { + // Allow join-accept messages. + bool join_accept = 1; + // Allow downlink messages with FPort of 0. + bool mac_data = 2; + // Allow downlink messages with FPort between 1 and 255. + bool application_data = 3; +} + +message PacketBrokerDefaultRoutingPolicy { + // Timestamp when the policy got last updated. + google.protobuf.Timestamp updated_at = 1; + // Uplink policy. + PacketBrokerRoutingPolicyUplink uplink = 2; + // Downlink policy. + PacketBrokerRoutingPolicyDownlink downlink = 3; +} + +message PacketBrokerRoutingPolicy { + // Packet Broker identifier of the Forwarder. + PacketBrokerNetworkIdentifier forwarder_id = 1; + // Packet Broker identifier of the Home Network. + PacketBrokerNetworkIdentifier home_network_id = 2; + // Timestamp when the policy got last updated. + google.protobuf.Timestamp updated_at = 3; + // Uplink policy. + PacketBrokerRoutingPolicyUplink uplink = 4; + // Downlink policy. + PacketBrokerRoutingPolicyDownlink downlink = 5; +} + +message SetPacketBrokerDefaultRoutingPolicyRequest { + // Uplink policy. + PacketBrokerRoutingPolicyUplink uplink = 1 [(validate.rules).message.required = true]; + // Downlink policy. + PacketBrokerRoutingPolicyDownlink downlink = 2 [(validate.rules).message.required = true]; +} + +message ListHomeNetworkRoutingPoliciesRequest { + // Limit the number of results per page. + uint32 limit = 1 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 2; +} + +message PacketBrokerRoutingPolicies { + repeated PacketBrokerRoutingPolicy policies = 1; +} + +message SetPacketBrokerRoutingPolicyRequest { + // Packet Broker identifier of the Home Network. + PacketBrokerNetworkIdentifier home_network_id = 1; + // Uplink policy. + PacketBrokerRoutingPolicyUplink uplink = 2 [(validate.rules).message.required = true]; + // Downlink policy. + PacketBrokerRoutingPolicyDownlink downlink = 3 [(validate.rules).message.required = true]; +} + +message PacketBrokerGatewayVisibility { + option (thethings.flags.message) = { select: false, set: true }; + // Show location. + bool location = 1; + // Show antenna placement (indoor/outdoor). + bool antenna_placement = 2; + // Show antenna count. + bool antenna_count = 3; + // Show whether the gateway produces fine timestamps. + bool fine_timestamps = 4; + // Show contact information. + bool contact_info = 5; + // Show status (online/offline). + bool status = 6; + // Show frequency plan. + bool frequency_plan = 8; + // Show receive and transmission packet rates. + bool packet_rates = 9; +} + +message PacketBrokerDefaultGatewayVisibility { + // Timestamp when the policy got last updated. + google.protobuf.Timestamp updated_at = 1; + PacketBrokerGatewayVisibility visibility = 2; +} + +message SetPacketBrokerDefaultGatewayVisibilityRequest { + PacketBrokerGatewayVisibility visibility = 1 [(validate.rules).message.required = true]; +} + +message ListPacketBrokerNetworksRequest { + // Limit the number of results per page. + uint32 limit = 1 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 2; + // If true, list only the Forwarders and Home Networks with whom a routing policy has been defined in either direction. + bool with_routing_policy = 3; + // Filter by tenant ID. + string tenant_id_contains = 4 [(validate.rules).string.max_len = 100]; + // Filter by name. + string name_contains = 5 [(validate.rules).string.max_len = 100]; +} + +message ListPacketBrokerHomeNetworksRequest { + // Limit the number of results per page. + uint32 limit = 1 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 2; + // Filter by tenant ID. + string tenant_id_contains = 3 [(validate.rules).string.max_len = 100]; + // Filter by name. + string name_contains = 4 [(validate.rules).string.max_len = 100]; +} + +message ListForwarderRoutingPoliciesRequest { + // Packet Broker identifier of the Home Network. + PacketBrokerNetworkIdentifier home_network_id = 1; + // Limit the number of results per page. + uint32 limit = 2; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 3; +} + +// The Pba service allows clients to manage peering through Packet Broker. +service Pba { + // Get information about the Packet Broker registration. + // Viewing Packet Packet information requires administrative access. + rpc GetInfo(google.protobuf.Empty) returns (PacketBrokerInfo) { + option (google.api.http) = { + get: "/pba/info" + }; + }; + + // Register with Packet Broker. If no registration exists, it will be created. Any existing registration will be updated. + // Registration settings not in the request message are taken from Packet Broker Agent configuration and caller context. + // Packet Broker registration requires administrative access. + // Packet Broker registration is only supported for tenants and requires Packet Broker Agent to be configured with + // NetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled. + rpc Register(PacketBrokerRegisterRequest) returns (PacketBrokerNetwork) { + option (google.api.http) = { + put: "/pba/registration" + body: "*" + additional_bindings { + post: "/pba/registration" + body: "*" + } + }; + }; + + // Deregister from Packet Broker. + // Packet Broker deregistration requires administrative access. + // Packet Broker deregistration is only supported for tenants and requires Packet Broker Agent to be configured with + // NetID level authentication. Use rpc GetInfo and check register_enabled to check whether this rpc is enabled. + rpc Deregister(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/pba/registration" + }; + }; + + // Get the default routing policy. + // Getting routing policies requires administrative access. + rpc GetHomeNetworkDefaultRoutingPolicy(google.protobuf.Empty) returns (PacketBrokerDefaultRoutingPolicy) { + option (google.api.http) = { + get: "/pba/home-networks/policies/default" + }; + }; + + // Set the default routing policy. + // Setting routing policies requires administrative access. + rpc SetHomeNetworkDefaultRoutingPolicy(SetPacketBrokerDefaultRoutingPolicyRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/pba/home-networks/policies/default" + body: "*" + additional_bindings { + post: "/pba/home-networks/policies/default" + body: "*" + } + }; + }; + + // Deletes the default routing policy. + // Deleting routing policies requires administrative access. + rpc DeleteHomeNetworkDefaultRoutingPolicy(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/pba/home-networks/policies/default" + }; + }; + + // List the routing policies that Packet Broker Agent as Forwarder configured with Home Networks. + // Listing routing policies requires administrative access. + rpc ListHomeNetworkRoutingPolicies(ListHomeNetworkRoutingPoliciesRequest) returns (PacketBrokerRoutingPolicies) { + option (google.api.http) = { + get: "/pba/home-networks/policies" + }; + }; + + // Get the routing policy for the given Home Network. + // Getting routing policies requires administrative access. + rpc GetHomeNetworkRoutingPolicy(PacketBrokerNetworkIdentifier) returns (PacketBrokerRoutingPolicy) { + option (google.api.http) = { + get: "/pba/home-networks/policies/{net_id}" + additional_bindings { + get: "/pba/home-networks/policies/{net_id}/{tenant_id}" + } + }; + }; + + // Set the routing policy for the given Home Network. + // Setting routing policies requires administrative access. + rpc SetHomeNetworkRoutingPolicy(SetPacketBrokerRoutingPolicyRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/pba/home-networks/policies/{home_network_id.net_id}" + body: "*" + additional_bindings { + post: "/pba/home-networks/policies/{home_network_id.net_id}" + body: "*" + } + additional_bindings { + put: "/pba/home-networks/policies/{home_network_id.net_id}/{home_network_id.tenant_id}" + body: "*" + } + additional_bindings { + post: "/pba/home-networks/policies/{home_network_id.net_id}/{home_network_id.tenant_id}" + body: "*" + } + }; + }; + + // Delete the routing policy for the given Home Network. + // Deleting routing policies requires administrative access. + rpc DeleteHomeNetworkRoutingPolicy(PacketBrokerNetworkIdentifier) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/pba/home-networks/policies/{net_id}" + additional_bindings { + delete: "/pba/home-networks/policies/{net_id}/{tenant_id}" + } + }; + }; + + // Get the default gateway visibility. + // Getting gateway visibilities requires administrative access. + rpc GetHomeNetworkDefaultGatewayVisibility(google.protobuf.Empty) returns (PacketBrokerDefaultGatewayVisibility) { + option (google.api.http) = { + get: "/pba/home-networks/gateway-visibilities/default" + }; + }; + + // Set the default gateway visibility. + // Setting gateway visibilities requires administrative access. + rpc SetHomeNetworkDefaultGatewayVisibility(SetPacketBrokerDefaultGatewayVisibilityRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/pba/home-networks/gateway-visibilities/default" + body: "*" + additional_bindings { + post: "/pba/home-networks/gateway-visibilities/default" + body: "*" + } + }; + }; + + // Deletes the default gateway visibility. + // Deleting gateway visibilities requires administrative access. + rpc DeleteHomeNetworkDefaultGatewayVisibility(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/pba/home-networks/gateway-visibilities/default" + }; + }; + + // List all listed networks. + // Listing networks requires administrative access. + rpc ListNetworks(ListPacketBrokerNetworksRequest) returns (PacketBrokerNetworks) { + option (google.api.http) = { + get: "/pba/networks" + }; + }; + + // List the listed home networks for which routing policies can be configured. + // Listing home networks requires administrative access. + rpc ListHomeNetworks(ListPacketBrokerHomeNetworksRequest) returns (PacketBrokerNetworks) { + option (google.api.http) = { + get: "/pba/home-networks" + }; + }; + + // List the routing policies that Forwarders configured with Packet Broker Agent as Home Network. + // Listing routing policies requires administrative access. + rpc ListForwarderRoutingPolicies(ListForwarderRoutingPoliciesRequest) returns (PacketBrokerRoutingPolicies) { + option (google.api.http) = { + get: "/pba/forwarders/policies" + }; + }; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/picture.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/picture.proto index 059a547d8..b0357385a 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/picture.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/picture.proto @@ -15,18 +15,22 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message Picture { message Embedded { // MIME type of the picture. - string mime_type = 1; + string mime_type = 1 [(validate.rules).string.max_len = 32]; // Picture data. A data URI can be constructed as follows: // `data:;base64,`. - bytes data = 2; + bytes data = 2 [(validate.rules).bytes.max_len = 8388608]; } // Embedded picture. diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/qrcodegenerator.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/qrcodegenerator.proto index 3de62199e..b23402e34 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/qrcodegenerator.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/qrcodegenerator.proto @@ -26,47 +26,62 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; -message QRCodeFormat { - option (gogoproto.populate) = false; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; +message QRCodeFormat { string name = 1 [(validate.rules).string.max_len = 100]; string description = 2 [(validate.rules).string.max_len = 200]; // The entity fields required to generate the QR code. - google.protobuf.FieldMask field_mask = 3 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 3; } message QRCodeFormats { - option (gogoproto.populate) = false; - + // Available formats. The map key is the format identifier. map formats = 1 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; } message GetQRCodeFormatRequest { - option (gogoproto.populate) = false; - - string format_id = 1 [(gogoproto.customname) = "FormatID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + // QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service. + string format_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; } message GenerateEndDeviceQRCodeRequest { - option (gogoproto.populate) = false; - - string format_id = 1 [(gogoproto.customname) = "FormatID", (validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; - EndDevice end_device = 2 [(gogoproto.nullable) = false, (validate.rules).message.required = true]; + // QR code format identifier. Enumerate available formats with rpc ListFormats in the EndDeviceQRCodeGenerator service. + string format_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36}]; + // End device to use as input to generate the QR code. + EndDevice end_device = 2 [(validate.rules).message.required = true]; message Image { + // Requested QR code image dimension in pixels. uint32 image_size = 1 [(validate.rules).uint32 = {gte: 10, lte: 1000}]; } + // If set, the server will render the QR code image according to these settings. Image image = 3; } message GenerateQRCodeResponse { - option (gogoproto.populate) = false; - + // Text representation of the QR code contents. string text = 1; // QR code in PNG format, if requested. Picture image = 2; } +message ParseEndDeviceQRCodeRequest { + // QR code format identifier. + // Enumerate available formats with the rpc `ListFormats`. + // If this field is not specified, the server will attempt to parse the data with each known format. + string format_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$|^$", max_len: 36}]; + // Raw QR code contents. + bytes qr_code = 2 [(validate.rules).bytes = {min_len: 10, max_len: 1024}]; +} + +message ParseEndDeviceQRCodeResponse { + // Identifier of the format used to successfully parse the QR code data. + string format_id = 1; + EndDeviceTemplate end_device_template = 2; +} + service EndDeviceQRCodeGenerator { // Return the QR code format. rpc GetFormat(GetQRCodeFormatRequest) returns (QRCodeFormat) { @@ -89,4 +104,16 @@ service EndDeviceQRCodeGenerator { body: "*" }; }; + + // Parse QR Codes of known formats and return the information contained within. + rpc Parse(ParseEndDeviceQRCodeRequest) returns (ParseEndDeviceQRCodeResponse){ + option (google.api.http) = { + post: "/qr-code/end-devices/parse", + body: "*" + additional_bindings { + post: "/qr-code/end-devices/{format_id}/parse" + body: "*" + } + }; + } } diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/regional.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/regional.proto index 9a9ca5a4a..2dbc25de4 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/regional.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/regional.proto @@ -22,6 +22,9 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + message ConcentratorConfig { message Channel { // Frequency (Hz). @@ -38,22 +41,22 @@ message ConcentratorConfig { uint32 bandwidth = 3; uint32 spreading_factor = 4; } - LoRaStandardChannel lora_standard_channel = 2 [(gogoproto.customname) = "LoRaStandardChannel"]; + LoRaStandardChannel lora_standard_channel = 2; message FSKChannel { // Frequency (Hz). uint64 frequency = 1; uint32 radio = 2; } - FSKChannel fsk_channel = 3 [(gogoproto.customname) = "FSKChannel"]; + FSKChannel fsk_channel = 3; message LBTConfiguration { // Received signal strength (dBm). - float rssi_target = 1 [(gogoproto.customname) = "RSSITarget"]; + float rssi_target = 1; // Received signal strength offset (dBm). - float rssi_offset = 2 [(gogoproto.customname) = "RSSIOffset"]; - google.protobuf.Duration scan_time = 3 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; + float rssi_offset = 2; + google.protobuf.Duration scan_time = 3; } - LBTConfiguration lbt = 4 [(gogoproto.customname) = "LBT"]; + LBTConfiguration lbt = 4; Channel ping_slot = 5; diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/rights.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/rights.proto index 8a343da96..ea37053de 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/rights.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/rights.proto @@ -16,15 +16,21 @@ syntax = "proto3"; package ttn.lorawan.v3; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto"; +import "google/protobuf/timestamp.proto"; import "lorawan-stack/api/identifiers.proto"; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // Right is the enum that defines all the different rights to do something in the network. enum Right { - option (gogoproto.goproto_enum_prefix) = false; + option (thethings.json.enum) = { marshal_as_string: true, prefix: "RIGHT" }; right_invalid = 0; @@ -54,6 +60,8 @@ enum Right { RIGHT_USER_ORGANIZATIONS_LIST = 12; // The right to create an organization under the user account. RIGHT_USER_ORGANIZATIONS_CREATE = 13; + // The right to read notifications sent to the user. + RIGHT_USER_NOTIFICATIONS_READ = 59; // The pseudo-right for all (current and future) user rights. RIGHT_USER_ALL = 14; @@ -88,13 +96,22 @@ enum Right { // The right to link as Application to a Network Server for traffic exchange, // i.e. read uplink and write downlink (API keys only). // This right is typically only given to an Application Server. - // This right implies RIGHT_APPLICATION_INFO. + // This right implies RIGHT_APPLICATION_INFO, RIGHT_APPLICATION_TRAFFIC_READ, + // and RIGHT_APPLICATION_TRAFFIC_DOWN_WRITE. RIGHT_APPLICATION_LINK = 27; // The pseudo-right for all (current and future) application rights. RIGHT_APPLICATION_ALL = 28; // The pseudo-right for all (current and future) OAuth client rights. RIGHT_CLIENT_ALL = 29; + // The right to read client information. + RIGHT_CLIENT_INFO = 60; + // The right to edit basic client settings. + RIGHT_CLIENT_SETTINGS_BASIC = 61; + // The right to view and edit client collaborators. + RIGHT_CLIENT_SETTINGS_COLLABORATORS = 62; + // The right to delete a client. + RIGHT_CLIENT_DELETE = 63; // The right to view gateway information. RIGHT_GATEWAY_INFO = 30; @@ -119,6 +136,10 @@ enum Right { RIGHT_GATEWAY_STATUS_READ = 38; // The right to view view gateway location. RIGHT_GATEWAY_LOCATION_READ = 39; + // The right to store secrets associated with this gateway. + RIGHT_GATEWAY_WRITE_SECRETS = 57; + // The right to retrieve secrets associated with this gateway. + RIGHT_GATEWAY_READ_SECRETS = 58; // The pseudo-right for all (current and future) gateway rights. RIGHT_GATEWAY_ALL = 40; @@ -155,6 +176,8 @@ enum Right { // The pseudo-right for all (current and future) possible rights. RIGHT_ALL = 55; + + // Next value: 64 } message Rights { @@ -162,9 +185,10 @@ message Rights { } message APIKey { + option (thethings.flags.message) = { select: true, set: true }; // Immutable and unique public identifier for the API key. // Generated by the Access Server. - string id = 1 [(gogoproto.customname) = "ID"]; + string id = 1; // Immutable and unique secret value of the API key. // Generated by the Access Server. string key = 2; @@ -174,14 +198,18 @@ message APIKey { // Rights that are granted to this API key. repeated Right rights = 4 [(validate.rules).repeated.items.enum.defined_only = true]; + + google.protobuf.Timestamp created_at = 5; + google.protobuf.Timestamp updated_at = 6; + google.protobuf.Timestamp expires_at = 7 [(validate.rules).timestamp.gt_now = true]; } message APIKeys { - repeated APIKey api_keys = 1 [(gogoproto.customname) = "APIKeys"]; + repeated APIKey api_keys = 1; } message Collaborator { - OrganizationOrUserIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + OrganizationOrUserIdentifiers ids = 1 [(validate.rules).message.required = true]; repeated Right rights = 2 [(validate.rules).repeated.items.enum.defined_only = true]; reserved 3; // reserved for future EntityIdentifiers entity = 3; reserved 4; // reserved for future State state = 4; @@ -191,7 +219,7 @@ message Collaborator { } message GetCollaboratorResponse { - OrganizationOrUserIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; + OrganizationOrUserIdentifiers ids = 1; repeated Right rights = 2; reserved 3; // reserved for future EntityIdentifiers entity = 3; reserved 4; // reserved for future State state = 4; diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/search_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/search_services.proto index 701ad4335..6893e3c91 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/search_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/search_services.proto @@ -16,11 +16,13 @@ syntax = "proto3"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "google/api/annotations.proto"; import "google/protobuf/field_mask.proto"; import "lorawan-stack/api/application.proto"; import "lorawan-stack/api/client.proto"; import "lorawan-stack/api/end_device.proto"; +import "lorawan-stack/api/enums.proto"; import "lorawan-stack/api/gateway.proto"; import "lorawan-stack/api/identifiers.proto"; import "lorawan-stack/api/organization.proto"; @@ -30,99 +32,382 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; -// This message is used for finding entities in the EntityRegistrySearch service. -message SearchEntitiesRequest { - // Find entities where the ID contains this substring. - string id_contains = 1 [(gogoproto.customname) = "IDContains"]; - // Find entities where the name contains this substring. - string name_contains = 2; - // Find entities where the description contains this substring. - string description_contains = 3; - // Find entities where the given attributes contain these substrings. - map attributes_contain = 4 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; - reserved 5; // TODO: Add filter for approval state (admin only). +// This message is used for finding applications in the EntityRegistrySearch service. +message SearchApplicationsRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Find applications where the ID, name or description contains this substring. + string query = 11 [ + (validate.rules).string.max_len = 50 + ]; + // Find applications where the ID contains this substring. + string id_contains = 1 [ + (validate.rules).string.max_len = 50 + ]; + // Find applications where the name contains this substring. + string name_contains = 2 [(validate.rules).string.max_len = 50]; + // Find applications where the description contains this substring. + string description_contains = 3 [(validate.rules).string.max_len = 50]; + // Find applications where the given attributes contain these substrings. + map attributes_contain = 4 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; - google.protobuf.FieldMask field_mask = 6 [(gogoproto.nullable) = false]; + reserved 5; // Reserved for EUI or state filter. + + google.protobuf.FieldMask field_mask = 6; + + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 7 [ + (validate.rules).string = { in: ["", "application_id", "-application_id", "name", "-name", "created_at", "-created_at"] } + ]; + // Limit the number of results per page. + uint32 limit = 8 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 9; + // Only return recently deleted applications. + bool deleted = 10; + + // next: 12 +} + +// This message is used for finding OAuth clients in the EntityRegistrySearch service. +message SearchClientsRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Find OAuth clients where the ID, name or description contains this substring. + string query = 11 [ + (validate.rules).string.max_len = 50 + ]; + // Find OAuth clients where the ID contains this substring. + string id_contains = 1 [ + (validate.rules).string.max_len = 50 + ]; + // Find OAuth clients where the name contains this substring. + string name_contains = 2 [(validate.rules).string.max_len = 50]; + // Find OAuth clients where the description contains this substring. + string description_contains = 3 [(validate.rules).string.max_len = 50]; + // Find OAuth clients where the given attributes contain these substrings. + map attributes_contain = 4 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; + + // Find OAuth clients where the state is any of these states. + repeated State state = 5 [ + (validate.rules).repeated = { + unique: true, + items: { enum: { defined_only: true } } + } + ]; + + google.protobuf.FieldMask field_mask = 6; + + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 7 [ + (validate.rules).string = { in: ["", "client_id", "-client_id", "name", "-name", "created_at", "-created_at"] } + ]; + // Limit the number of results per page. + uint32 limit = 8 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 9; + // Only return recently deleted OAuth clients. + bool deleted = 10; + + // next: 12 +} + +// This message is used for finding gateways in the EntityRegistrySearch service. +message SearchGatewaysRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Find gateways where the ID, name, description or EUI contains this substring. + string query = 11 [ + (validate.rules).string.max_len = 50 + ]; + // Find gateways where the ID contains this substring. + string id_contains = 1 [ + (validate.rules).string.max_len = 50 + ]; + // Find gateways where the name contains this substring. + string name_contains = 2 [(validate.rules).string.max_len = 50]; + // Find gateways where the description contains this substring. + string description_contains = 3 [(validate.rules).string.max_len = 50]; + // Find gateways where the given attributes contain these substrings. + map attributes_contain = 4 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; + + // Find gateways where the (hexadecimal) EUI contains this substring. + string eui_contains = 5 [ + (validate.rules).string.max_len = 16 + ]; + + google.protobuf.FieldMask field_mask = 6; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. - string order = 7; + string order = 7 [ + (validate.rules).string = { in: ["", "gateway_id", "-gateway_id", "gateway_eui", "-gateway_eui", "name", "-name", "created_at", "-created_at"] } + ]; // Limit the number of results per page. uint32 limit = 8 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 9; + // Only return recently deleted gateways. + bool deleted = 10; + + // next: 12 +} + +// This message is used for finding organizations in the EntityRegistrySearch service. +message SearchOrganizationsRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Find organizations where the ID, name or description contains this substring. + string query = 11 [ + (validate.rules).string.max_len = 50 + ]; + // Find organizations where the ID contains this substring. + string id_contains = 1 [ + (validate.rules).string.max_len = 50 + ]; + // Find organizations where the name contains this substring. + string name_contains = 2 [(validate.rules).string.max_len = 50]; + // Find organizations where the description contains this substring. + string description_contains = 3 [(validate.rules).string.max_len = 50]; + // Find organizations where the given attributes contain these substrings. + map attributes_contain = 4 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; + + reserved 5; // Reserved for state filter. + + google.protobuf.FieldMask field_mask = 6; + + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 7 [ + (validate.rules).string = { in: ["", "organization_id", "-organization_id", "name", "-name", "created_at", "-created_at"] } + ]; + // Limit the number of results per page. + uint32 limit = 8 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 9; + // Only return recently deleted organizations. + bool deleted = 10; + + // next: 12 +} + +// This message is used for finding users in the EntityRegistrySearch service. +message SearchUsersRequest { + option (thethings.flags.message) = { select: false, set: true }; + // Find users where the ID, name or description contains this substring. + string query = 11 [ + (validate.rules).string.max_len = 50 + ]; + // Find users where the ID contains this substring. + string id_contains = 1 [ + (validate.rules).string.max_len = 50 + ]; + // Find users where the name contains this substring. + string name_contains = 2 [(validate.rules).string.max_len = 50]; + // Find users where the description contains this substring. + string description_contains = 3 [(validate.rules).string.max_len = 50]; + // Find users where the given attributes contain these substrings. + map attributes_contain = 4 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; + + // Find users where the state is any of these states. + repeated State state = 5 [ + (validate.rules).repeated = { + unique: true, + items: { enum: { defined_only: true } } + } + ]; + + google.protobuf.FieldMask field_mask = 6; + // Order the results by this field path (must be present in the field mask). + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 7 [ + (validate.rules).string = { in: ["", "user_id", "-user_id", "name", "-name", "primary_email_address", "-primary_email_address", "state", "-state", "admin", "-admin", "created_at", "-created_at"] } + ]; + // Limit the number of results per page. + uint32 limit = 8 [(validate.rules).uint32.lte = 1000]; + // Page number for pagination. 0 is interpreted as 1. + uint32 page = 9; + // Only return recently deleted users. + bool deleted = 10; +} + +message SearchAccountsRequest { + option (thethings.flags.message) = { select: false, set: true }; + + string query = 1 [ + (validate.rules).string = { max_len: 50 } + ]; + + bool only_users = 2; + + oneof collaborator_of { + ApplicationIdentifiers application_ids = 3; + ClientIdentifiers client_ids = 4; + GatewayIdentifiers gateway_ids = 5; + OrganizationIdentifiers organization_ids = 6; + } + + // NOTE: This request intentionally does not support pagination. +} + +message SearchAccountsResponse { + repeated OrganizationOrUserIdentifiers account_ids = 1; } // The EntityRegistrySearch service indexes entities in the various registries // and enables searching for them. // This service is not implemented on all deployments. service EntityRegistrySearch { - rpc SearchApplications(SearchEntitiesRequest) returns (Applications) { + // Search for applications that match the conditions specified in the request. + // Non-admin users will only match applications that they have rights on. + rpc SearchApplications(SearchApplicationsRequest) returns (Applications) { option (google.api.http) = { get: "/search/applications" }; } - rpc SearchClients(SearchEntitiesRequest) returns (Clients) { + // Search for OAuth clients that match the conditions specified in the request. + // Non-admin users will only match OAuth clients that they have rights on. + rpc SearchClients(SearchClientsRequest) returns (Clients) { option (google.api.http) = { get: "/search/clients" }; } - rpc SearchGateways(SearchEntitiesRequest) returns (Gateways) { + // Search for gateways that match the conditions specified in the request. + // Non-admin users will only match gateways that they have rights on. + rpc SearchGateways(SearchGatewaysRequest) returns (Gateways) { option (google.api.http) = { get: "/search/gateways" }; } - rpc SearchOrganizations(SearchEntitiesRequest) returns (Organizations) { + // Search for organizations that match the conditions specified in the request. + // Non-admin users will only match organizations that they have rights on. + rpc SearchOrganizations(SearchOrganizationsRequest) returns (Organizations) { option (google.api.http) = { get: "/search/organizations" }; } - rpc SearchUsers(SearchEntitiesRequest) returns (Users) { + // Search for users that match the conditions specified in the request. + // This is only available to admin users. + rpc SearchUsers(SearchUsersRequest) returns (Users) { option (google.api.http) = { get: "/search/users" }; } + + // Search for accounts that match the conditions specified in the request. + rpc SearchAccounts(SearchAccountsRequest) returns (SearchAccountsResponse) { + option (google.api.http) = { + get: "/search/accounts" + additional_bindings { + get: "/applications/{application_ids.application_id}/collaborators/search" + } + additional_bindings { + get: "/clients/{client_ids.client_id}/collaborators/search" + } + additional_bindings { + get: "/gateways/{gateway_ids.gateway_id}/collaborators/search" + } + additional_bindings { + get: "/organizations/{organization_ids.organization_id}/collaborators/search" + } + }; + } } message SearchEndDevicesRequest { - ApplicationIdentifiers application_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: false, set: true }; + ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true]; + + // Find end devices where the ID, name, description or EUI contains this substring. + string query = 13 [ + (validate.rules).string.max_len = 50 + ]; // Find end devices where the ID contains this substring. - string id_contains = 2 [(gogoproto.customname) = "IDContains"]; + string id_contains = 2 [ + (validate.rules).string.max_len = 50 + ]; // Find end devices where the name contains this substring. - string name_contains = 3; + string name_contains = 3 [(validate.rules).string.max_len = 50]; // Find end devices where the description contains this substring. - string description_contains = 4; + string description_contains = 4 [(validate.rules).string.max_len = 50]; // Find end devices where the given attributes contain these substrings. - map attributes_contain = 5 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes_contain = 5 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 50 } } + } + ]; // Find end devices where the (hexadecimal) DevEUI contains this substring. - string dev_eui_contains = 6 [(gogoproto.customname) = "DevEUIContains"]; + string dev_eui_contains = 6 [ + (validate.rules).string.max_len = 16 + ]; // Find end devices where the (hexadecimal) JoinEUI contains this substring. - string join_eui_contains = 7 [(gogoproto.customname) = "JoinEUIContains"]; + string join_eui_contains = 7 [ + (validate.rules).string.max_len = 16 + ]; // Find end devices where the (hexadecimal) DevAddr contains this substring. - string dev_addr_contains = 8 [(gogoproto.customname) = "DevAddrContains"]; + string dev_addr_contains = 8 [ + (validate.rules).string.max_len = 8 + ]; - google.protobuf.FieldMask field_mask = 9 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 9; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. - string order = 10; + string order = 10 [ + (validate.rules).string = { in: ["", "device_id", "-device_id", "join_eui", "-join_eui", "dev_eui", "-dev_eui", "name", "-name", "description", "-description", "created_at", "-created_at"] } + ]; // Limit the number of results per page. uint32 limit = 11 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 12; + + // next: 14 } // The EndDeviceRegistrySearch service indexes devices in the EndDeviceRegistry // and enables searching for them. // This service is not implemented on all deployments. service EndDeviceRegistrySearch { + // Search for end devices in the given application that match the conditions specified in the request. rpc SearchEndDevices(SearchEndDevicesRequest) returns (EndDevices) { option (google.api.http) = { get: "/search/applications/{application_ids.application_id}/devices" diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/secrets.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/secrets.proto new file mode 100644 index 000000000..22090abd8 --- /dev/null +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/secrets.proto @@ -0,0 +1,40 @@ +// Copyright © 2020 The Things Network Foundation, The Things Industries B.V. +// +// 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. + +syntax = "proto3"; + +package ttn.lorawan.v3; + +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; + +option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; + +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// Secret contains a secret value. It also contains the ID of the Encryption key used to encrypt it. +message Secret { + option (thethings.flags.message) = { select: true, set: true }; + // ID of the Key used to encrypt the secret. + string key_id = 1; + bytes value = 2 [ + (validate.rules).bytes.max_len = 2048, + (thethings.flags.field) = { + set_flag_new_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.NewHexBytesFlag", + set_flag_getter_func: "github.com/TheThingsIndustries/protoc-gen-go-flags/flagsplugin.GetBytes" + } + ]; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/user.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/user.proto index 4ba20d42a..f4ad5b7ab 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/user.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/user.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto"; import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/protobuf/field_mask.proto"; @@ -28,50 +29,98 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + // User is the message that defines a user on the network. message User { - UserIdentifiers ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - google.protobuf.Timestamp created_at = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + option (thethings.flags.message) = { select: true, set: true }; + // The identifiers of the user. These are public and can be seen by any authenticated user in the network. + UserIdentifiers ids = 1 [ + (validate.rules).message.required = true, + (thethings.flags.field) = { select: false, hidden: true } + ]; + // When the user was created. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp created_at = 2 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the user was last updated. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp updated_at = 3 [ + (thethings.flags.field) = { select: false, set: false } + ]; + // When the user was deleted. This information is public and can be seen by any authenticated user in the network. + google.protobuf.Timestamp deleted_at = 19 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // The name of the user. This information is public and can be seen by any authenticated user in the network. string name = 4 [(validate.rules).string.max_len = 50]; + // A description for the user. This information is public and can be seen by any authenticated user in the network. string description = 5 [(validate.rules).string.max_len = 2000]; // Key-value attributes for this users. Typically used for storing integration-specific data. - map attributes = 6 [(validate.rules).map.keys.string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}]; + map attributes = 6 [ + (validate.rules).map = { + max_pairs: 10, + keys: { string: { pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$", max_len: 36 } }, + values: { string: { max_len: 200 } } + } + ]; // Contact information for this user. Typically used to indicate who to contact with security/billing questions about the user. - repeated ContactInfo contact_info = 7; + // This field is deprecated. + repeated ContactInfo contact_info = 7 [deprecated = true, (validate.rules).repeated.max_items = 10]; // Primary email address that can be used for logging in. // This address is not public, use contact_info for that. string primary_email_address = 8 [(validate.rules).string.email = true]; // When the primary email address was validated. Note that email address validation is not required on all networks. - google.protobuf.Timestamp primary_email_address_validated_at = 9 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp primary_email_address_validated_at = 9; // The password field is only considered when creating a user. // It is not returned on API calls, and can not be updated by updating the User. // See the UpdatePassword method of the UserRegistry service for more information. - string password = 10; - google.protobuf.Timestamp password_updated_at = 11 [(gogoproto.stdtime) = true]; + string password = 10 [(validate.rules).string.max_len = 1000]; + google.protobuf.Timestamp password_updated_at = 11 [ + (thethings.flags.field) = { select: true, set: false } + ]; bool require_password_update = 12; // The reviewing state of the user. + // This information is public and can be seen by any authenticated user in the network. // This field can only be modified by admins. State state = 13 [(validate.rules).enum.defined_only = true]; + // A description for the state field. + // This field can only be modified by admins, and should typically only be updated + // when also updating `state`. + string state_description = 20 [(validate.rules).string.max_len = 128]; // This user is an admin. + // This information is public and can be seen by any authenticated user in the network. // This field can only be modified by other admins. bool admin = 14; // The temporary password can only be used to update a user's password; never returned on API calls. // It is not returned on API calls, and can not be updated by updating the User. // See the CreateTemporaryPassword method of the UserRegistry service for more information. - string temporary_password = 15; - google.protobuf.Timestamp temporary_password_created_at = 16 [(gogoproto.stdtime) = true]; - google.protobuf.Timestamp temporary_password_expires_at = 17 [(gogoproto.stdtime) = true]; + string temporary_password = 15 [(validate.rules).string.max_len = 1000]; + google.protobuf.Timestamp temporary_password_created_at = 16 [ + (thethings.flags.field) = { select: true, set: false } + ]; + google.protobuf.Timestamp temporary_password_expires_at = 17 [ + (thethings.flags.field) = { select: true, set: false } + ]; + // A profile picture for the user. + // This information is public and can be seen by any authenticated user in the network. Picture profile_picture = 18; + + reserved 21; reserved "application_limit"; + reserved 22; reserved "client_limit"; + reserved 23; reserved "gateway_limit"; + reserved 24; reserved "organization_limit"; + + // next: 25 } message Users { @@ -79,14 +128,15 @@ message Users { } message GetUserRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; // The names of the user fields that should be returned. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message ListUsersRequest { + option (thethings.flags.message) = { select: true, set: true }; // The names of the user fields that should be returned. - google.protobuf.FieldMask field_mask = 1 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 1; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 2 [ @@ -96,34 +146,43 @@ message ListUsersRequest { uint32 limit = 3 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. uint32 page = 4; + // Only return recently deleted users. + bool deleted = 5; } message CreateUserRequest { - User user = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + User user = 1 [(validate.rules).message.required = true]; // The invitation token that was sent to the user (some networks require an invitation in order to register new users). string invitation_token = 2; } message UpdateUserRequest { - User user = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + User user = 1 [(validate.rules).message.required = true]; // The names of the user fields that should be updated. - google.protobuf.FieldMask field_mask = 2 [(gogoproto.nullable) = false]; + google.protobuf.FieldMask field_mask = 2; } message CreateTemporaryPasswordRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; } message UpdateUserPasswordRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string new = 2; - string old = 3; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + string new = 2 [(validate.rules).string.max_len = 1000]; + string old = 3 [(validate.rules).string.max_len = 1000]; // Revoke active sessions and access tokens of user if true. To be used if credentials are suspected to be compromised. bool revoke_all_access = 4; } message ListUserAPIKeysRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + option (thethings.flags.message) = { select: false, set: true }; + + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + // Order the results by this field path. + // Default ordering is by ID. Prepend with a minus (-) to reverse the order. + string order = 4 [ + (validate.rules).string = { in: ["", "api_key_id", "-api_key_id", "name", "-name", "created_at", "-created_at", "expires_at", "-expires_at"] } + ]; // Limit the number of results per page. uint32 limit = 2 [(validate.rules).uint32.lte = 1000]; // Page number for pagination. 0 is interpreted as 1. @@ -131,31 +190,40 @@ message ListUserAPIKeysRequest { } message GetUserAPIKeyRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; // Unique public identifier for the API key. - string key_id = 2 [(gogoproto.customname) = "KeyID"]; + string key_id = 2; } message CreateUserAPIKeyRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; string name = 2 [(validate.rules).string.max_len = 50]; - repeated Right rights = 3 [(validate.rules).repeated.items.enum.defined_only = true]; + repeated Right rights = 3 [ + (validate.rules).repeated = { + min_items: 1, + unique: true, + items: { enum: { defined_only: true } } + } + ]; + google.protobuf.Timestamp expires_at = 4 [(validate.rules).timestamp.gt_now = true]; } message UpdateUserAPIKeyRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - APIKey api_key = 2 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + APIKey api_key = 2 [(validate.rules).message.required = true]; + // The names of the api key fields that should be updated. + google.protobuf.FieldMask field_mask = 3; } message Invitation { string email = 1 [(validate.rules).string.email = true]; string token = 2; - google.protobuf.Timestamp expires_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp created_at = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + google.protobuf.Timestamp expires_at = 3; + google.protobuf.Timestamp created_at = 4; + google.protobuf.Timestamp updated_at = 5; - google.protobuf.Timestamp accepted_at = 6 [(gogoproto.stdtime) = true]; + google.protobuf.Timestamp accepted_at = 6; UserIdentifiers accepted_by = 7; } @@ -179,16 +247,16 @@ message DeleteInvitationRequest { } message UserSessionIdentifiers { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string session_id = 2 [(gogoproto.customname) = "SessionID", (validate.rules).string.max_len = 64]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + string session_id = 2 [(validate.rules).string.max_len = 64]; } message UserSession { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; - string session_id = 2 [(gogoproto.customname) = "SessionID", (validate.rules).string.max_len = 64]; - google.protobuf.Timestamp created_at = 3 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp updated_at = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - google.protobuf.Timestamp expires_at = 5 [(gogoproto.stdtime) = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + string session_id = 2 [(validate.rules).string.max_len = 64]; + google.protobuf.Timestamp created_at = 3; + google.protobuf.Timestamp updated_at = 4; + google.protobuf.Timestamp expires_at = 5; // The session secret is used to compose an authorization key and is never returned. string session_secret = 6; } @@ -198,7 +266,7 @@ message UserSessions { } message ListUserSessionsRequest { - UserIdentifiers user_ids = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false, (validate.rules).message.required = true]; + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; // Order the results by this field path (must be present in the field mask). // Default ordering is by ID. Prepend with a minus (-) to reverse the order. string order = 2 [ @@ -209,3 +277,27 @@ message ListUserSessionsRequest { // Page number for pagination. 0 is interpreted as 1. uint32 page = 4; } + +message LoginToken { + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + + google.protobuf.Timestamp created_at = 2; + google.protobuf.Timestamp updated_at = 3; + google.protobuf.Timestamp expires_at = 4; + + string token = 5; + bool used = 6; +} + +message CreateLoginTokenRequest { + UserIdentifiers user_ids = 1 [(validate.rules).message.required = true]; + // Skip sending the login token to the user by email. + // This field is only effective when the login token is created by an admin user. + bool skip_email = 2; +} + +message CreateLoginTokenResponse { + // The token that can be used for logging in as the user. + // This field is only present if a token was created by an admin user for a non-admin user. + string token = 1; +} diff --git a/lora-ns-ttn/src/main/proto/lorawan-stack/api/user_services.proto b/lora-ns-ttn/src/main/proto/lorawan-stack/api/user_services.proto index e73c51f97..3755c4d2b 100644 --- a/lora-ns-ttn/src/main/proto/lorawan-stack/api/user_services.proto +++ b/lora-ns-ttn/src/main/proto/lorawan-stack/api/user_services.proto @@ -14,6 +14,7 @@ syntax = "proto3"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; import "lorawan-stack/api/identifiers.proto"; @@ -24,6 +25,11 @@ package ttn.lorawan.v3; option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb"; +// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798). +option (gogoproto.goproto_registration) = true; + +// The UserRegistry service, exposed by the Identity Server, is used to manage +// user registrations. service UserRegistry { // Register a new user. This method may be restricted by network settings. rpc Create(CreateUserRequest) returns (User) { @@ -80,8 +86,29 @@ service UserRegistry { delete: "/users/{user_id}" }; }; + + // Restore a recently deleted user. + // + // Deployment configuration may specify if, and for how long after deletion, + // entities can be restored. + rpc Restore(UserIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/users/{user_id}/restore" + }; + }; + + // Purge the user. This will release the user ID for reuse. + // The user is responsible for clearing data from any (external) integrations + // that may store and expose data by user or organization ID. + rpc Purge(UserIdentifiers) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{user_id}/purge" + }; + }; } +// The UserAcces service, exposed by the Identity Server, is used to manage +// API keys of users. service UserAccess { // List the rights the caller has on this user. rpc ListRights(UserIdentifiers) returns (Rights) { @@ -123,6 +150,13 @@ service UserAccess { body: "*" }; }; + + // Create a login token that can be used for a one-time login as a user. + rpc CreateLoginToken(CreateLoginTokenRequest) returns (CreateLoginTokenResponse) { + option (google.api.http) = { + post: "/users/{user_ids.user_id}/login-tokens" + }; + } } service UserInvitationRegistry { @@ -149,6 +183,8 @@ service UserInvitationRegistry { }; } +// The UserSessionRegistry service, exposed by the Identity Server, is used to manage +// (browser) sessions of the user. service UserSessionRegistry { // List the active sessions for the given user. rpc List(ListUserSessionsRequest) returns (UserSessions) { diff --git a/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/annotations.proto b/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/annotations.proto new file mode 100644 index 000000000..d63d3c87e --- /dev/null +++ b/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/annotations.proto @@ -0,0 +1,44 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/descriptor.proto"; +import "protoc-gen-openapiv2/options/openapiv2.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +extend google.protobuf.FileOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Swagger openapiv2_swagger = 1042; +} +extend google.protobuf.MethodOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Operation openapiv2_operation = 1042; +} +extend google.protobuf.MessageOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Schema openapiv2_schema = 1042; +} +extend google.protobuf.ServiceOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + Tag openapiv2_tag = 1042; +} +extend google.protobuf.FieldOptions { + // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project. + // + // All IDs are the same, as assigned. It is okay that they are the same, as they extend + // different descriptor messages. + JSONSchema openapiv2_field = 1042; +} diff --git a/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/openapiv2.proto b/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/openapiv2.proto new file mode 100644 index 000000000..36a2b978f --- /dev/null +++ b/lora-ns-ttn/src/main/proto/protoc-gen-openapiv2/options/openapiv2.proto @@ -0,0 +1,659 @@ +syntax = "proto3"; + +package grpc.gateway.protoc_gen_openapiv2.options; + +import "google/protobuf/struct.proto"; + +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"; + +// Scheme describes the schemes supported by the OpenAPI Swagger +// and Operation objects. +enum Scheme { + UNKNOWN = 0; + HTTP = 1; + HTTPS = 2; + WS = 3; + WSS = 4; +} + +// `Swagger` is a representation of OpenAPI v2 specification's Swagger object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// }; +// schemes: HTTPS; +// consumes: "application/json"; +// produces: "application/json"; +// }; +// +message Swagger { + // Specifies the OpenAPI Specification version being used. It can be + // used by the OpenAPI UI and other clients to interpret the API listing. The + // value MUST be "2.0". + string swagger = 1; + // Provides metadata about the API. The metadata can be used by the + // clients if needed. + Info info = 2; + // The host (name or ip) serving the API. This MUST be the host only and does + // not include the scheme nor sub-paths. It MAY include a port. If the host is + // not included, the host serving the documentation is to be used (including + // the port). The host does not support path templating. + string host = 3; + // The base path on which the API is served, which is relative to the host. If + // it is not included, the API is served directly under the host. The value + // MUST start with a leading slash (/). The basePath does not support path + // templating. + // Note that using `base_path` does not change the endpoint paths that are + // generated in the resulting OpenAPI file. If you wish to use `base_path` + // with relatively generated OpenAPI paths, the `base_path` prefix must be + // manually removed from your `google.api.http` paths and your code changed to + // serve the API from the `base_path`. + string base_path = 4; + // The transfer protocol of the API. Values MUST be from the list: "http", + // "https", "ws", "wss". If the schemes is not included, the default scheme to + // be used is the one used to access the OpenAPI definition itself. + repeated Scheme schemes = 5; + // A list of MIME types the APIs can consume. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string consumes = 6; + // A list of MIME types the APIs can produce. This is global to all APIs but + // can be overridden on specific API calls. Value MUST be as described under + // Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'paths'. + reserved 8; + // field 9 is reserved for 'definitions', which at this time are already + // exposed as and customizable as proto messages. + reserved 9; + // An object to hold responses that can be used across operations. This + // property does not define global responses for all operations. + map responses = 10; + // Security scheme definitions that can be used across the specification. + SecurityDefinitions security_definitions = 11; + // A declaration of which security schemes are applied for the API as a whole. + // The list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). + // Individual operations can override this definition. + repeated SecurityRequirement security = 12; + // field 13 is reserved for 'tags', which are supposed to be exposed as and + // customizable as proto services. TODO(ivucica): add processing of proto + // service objects into OpenAPI v2 Tag objects. + reserved 13; + // Additional external documentation. + ExternalDocumentation external_docs = 14; + map extensions = 15; +} + +// `Operation` is a representation of OpenAPI v2 specification's Operation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject +// +// Example: +// +// service EchoService { +// rpc Echo(SimpleMessage) returns (SimpleMessage) { +// option (google.api.http) = { +// get: "/v1/example/echo/{id}" +// }; +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { +// summary: "Get a message."; +// operation_id: "getMessage"; +// tags: "echo"; +// responses: { +// key: "200" +// value: { +// description: "OK"; +// } +// } +// }; +// } +// } +message Operation { + // A list of tags for API documentation control. Tags can be used for logical + // grouping of operations by resources or any other qualifier. + repeated string tags = 1; + // A short summary of what the operation does. For maximum readability in the + // swagger-ui, this field SHOULD be less than 120 characters. + string summary = 2; + // A verbose explanation of the operation behavior. GFM syntax can be used for + // rich text representation. + string description = 3; + // Additional external documentation for this operation. + ExternalDocumentation external_docs = 4; + // Unique string used to identify the operation. The id MUST be unique among + // all operations described in the API. Tools and libraries MAY use the + // operationId to uniquely identify an operation, therefore, it is recommended + // to follow common programming naming conventions. + string operation_id = 5; + // A list of MIME types the operation can consume. This overrides the consumes + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string consumes = 6; + // A list of MIME types the operation can produce. This overrides the produces + // definition at the OpenAPI Object. An empty value MAY be used to clear the + // global definition. Value MUST be as described under Mime Types. + repeated string produces = 7; + // field 8 is reserved for 'parameters'. + reserved 8; + // The list of possible responses as they are returned from executing this + // operation. + map responses = 9; + // The transfer protocol for the operation. Values MUST be from the list: + // "http", "https", "ws", "wss". The value overrides the OpenAPI Object + // schemes definition. + repeated Scheme schemes = 10; + // Declares this operation to be deprecated. Usage of the declared operation + // should be refrained. Default value is false. + bool deprecated = 11; + // A declaration of which security schemes are applied for this operation. The + // list of values describes alternative security schemes that can be used + // (that is, there is a logical OR between the security requirements). This + // definition overrides any declared top-level security. To remove a top-level + // security declaration, an empty array can be used. + repeated SecurityRequirement security = 12; + map extensions = 13; +} + +// `Header` is a representation of OpenAPI v2 specification's Header object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject +// +message Header { + // `Description` is a short description of the header. + string description = 1; + // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported. + string type = 2; + // `Format` The extending format for the previously mentioned type. + string format = 3; + // field 4 is reserved for 'items', but in OpenAPI-specific way. + reserved 4; + // field 5 is reserved `Collection Format` Determines the format of the array if type array is used. + reserved 5; + // `Default` Declares the value of the header that the server will use if none is provided. + // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. + // Unlike JSON Schema this value MUST conform to the defined type for the header. + string default = 6; + // field 7 is reserved for 'maximum'. + reserved 7; + // field 8 is reserved for 'exclusiveMaximum'. + reserved 8; + // field 9 is reserved for 'minimum'. + reserved 9; + // field 10 is reserved for 'exclusiveMinimum'. + reserved 10; + // field 11 is reserved for 'maxLength'. + reserved 11; + // field 12 is reserved for 'minLength'. + reserved 12; + // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3. + string pattern = 13; + // field 14 is reserved for 'maxItems'. + reserved 14; + // field 15 is reserved for 'minItems'. + reserved 15; + // field 16 is reserved for 'uniqueItems'. + reserved 16; + // field 17 is reserved for 'enum'. + reserved 17; + // field 18 is reserved for 'multipleOf'. + reserved 18; +} + +// `Response` is a representation of OpenAPI v2 specification's Response object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject +// +message Response { + // `Description` is a short description of the response. + // GFM syntax can be used for rich text representation. + string description = 1; + // `Schema` optionally defines the structure of the response. + // If `Schema` is not provided, it means there is no content to the response. + Schema schema = 2; + // `Headers` A list of headers that are sent with the response. + // `Header` name is expected to be a string in the canonical format of the MIME header key + // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey + map headers = 3; + // `Examples` gives per-mimetype response examples. + // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object + map examples = 4; + map extensions = 5; +} + +// `Info` is a representation of OpenAPI v2 specification's Info object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// title: "Echo API"; +// version: "1.0"; +// description: ""; +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// }; +// ... +// }; +// +message Info { + // The title of the application. + string title = 1; + // A short description of the application. GFM syntax can be used for rich + // text representation. + string description = 2; + // The Terms of Service for the API. + string terms_of_service = 3; + // The contact information for the exposed API. + Contact contact = 4; + // The license information for the exposed API. + License license = 5; + // Provides the version of the application API (not to be confused + // with the specification version). + string version = 6; + map extensions = 7; +} + +// `Contact` is a representation of OpenAPI v2 specification's Contact object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// contact: { +// name: "gRPC-Gateway project"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// email: "none@example.com"; +// }; +// ... +// }; +// ... +// }; +// +message Contact { + // The identifying name of the contact person/organization. + string name = 1; + // The URL pointing to the contact information. MUST be in the format of a + // URL. + string url = 2; + // The email address of the contact person/organization. MUST be in the format + // of an email address. + string email = 3; +} + +// `License` is a representation of OpenAPI v2 specification's License object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// info: { +// ... +// license: { +// name: "BSD 3-Clause License"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt"; +// }; +// ... +// }; +// ... +// }; +// +message License { + // The license name used for the API. + string name = 1; + // A URL to the license used for the API. MUST be in the format of a URL. + string url = 2; +} + +// `ExternalDocumentation` is a representation of OpenAPI v2 specification's +// ExternalDocumentation object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject +// +// Example: +// +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +// ... +// external_docs: { +// description: "More about gRPC-Gateway"; +// url: "https://github.com/grpc-ecosystem/grpc-gateway"; +// } +// ... +// }; +// +message ExternalDocumentation { + // A short description of the target documentation. GFM syntax can be used for + // rich text representation. + string description = 1; + // The URL for the target documentation. Value MUST be in the format + // of a URL. + string url = 2; +} + +// `Schema` is a representation of OpenAPI v2 specification's Schema object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +message Schema { + JSONSchema json_schema = 1; + // Adds support for polymorphism. The discriminator is the schema property + // name that is used to differentiate between other schema that inherit this + // schema. The property name used MUST be defined at this schema and it MUST + // be in the required property list. When used, the value MUST be the name of + // this schema or any schema that inherits it. + string discriminator = 2; + // Relevant only for Schema "properties" definitions. Declares the property as + // "read only". This means that it MAY be sent as part of a response but MUST + // NOT be sent as part of the request. Properties marked as readOnly being + // true SHOULD NOT be in the required list of the defined schema. Default + // value is false. + bool read_only = 3; + // field 4 is reserved for 'xml'. + reserved 4; + // Additional external documentation for this schema. + ExternalDocumentation external_docs = 5; + // A free-form property to include an example of an instance for this schema in JSON. + // This is copied verbatim to the output. + string example = 6; +} + +// `JSONSchema` represents properties from JSON Schema taken, and as used, in +// the OpenAPI v2 spec. +// +// This includes changes made by OpenAPI v2. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject +// +// See also: https://cswr.github.io/JsonSchema/spec/basic_types/, +// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json +// +// Example: +// +// message SimpleMessage { +// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { +// json_schema: { +// title: "SimpleMessage" +// description: "A simple message." +// required: ["id"] +// } +// }; +// +// // Id represents the message identifier. +// string id = 1; [ +// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { +// description: "The unique identifier of the simple message." +// }]; +// } +// +message JSONSchema { + // field 1 is reserved for '$id', omitted from OpenAPI v2. + reserved 1; + // field 2 is reserved for '$schema', omitted from OpenAPI v2. + reserved 2; + // Ref is used to define an external reference to include in the message. + // This could be a fully qualified proto message reference, and that type must + // be imported into the protofile. If no message is identified, the Ref will + // be used verbatim in the output. + // For example: + // `ref: ".google.protobuf.Timestamp"`. + string ref = 3; + // field 4 is reserved for '$comment', omitted from OpenAPI v2. + reserved 4; + // The title of the schema. + string title = 5; + // A short description of the schema. + string description = 6; + string default = 7; + bool read_only = 8; + // A free-form property to include a JSON example of this field. This is copied + // verbatim to the output swagger.json. Quotes must be escaped. + // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject + string example = 9; + double multiple_of = 10; + // Maximum represents an inclusive upper limit for a numeric instance. The + // value of MUST be a number, + double maximum = 11; + bool exclusive_maximum = 12; + // minimum represents an inclusive lower limit for a numeric instance. The + // value of MUST be a number, + double minimum = 13; + bool exclusive_minimum = 14; + uint64 max_length = 15; + uint64 min_length = 16; + string pattern = 17; + // field 18 is reserved for 'additionalItems', omitted from OpenAPI v2. + reserved 18; + // field 19 is reserved for 'items', but in OpenAPI-specific way. + // TODO(ivucica): add 'items'? + reserved 19; + uint64 max_items = 20; + uint64 min_items = 21; + bool unique_items = 22; + // field 23 is reserved for 'contains', omitted from OpenAPI v2. + reserved 23; + uint64 max_properties = 24; + uint64 min_properties = 25; + repeated string required = 26; + // field 27 is reserved for 'additionalProperties', but in OpenAPI-specific + // way. TODO(ivucica): add 'additionalProperties'? + reserved 27; + // field 28 is reserved for 'definitions', omitted from OpenAPI v2. + reserved 28; + // field 29 is reserved for 'properties', but in OpenAPI-specific way. + // TODO(ivucica): add 'additionalProperties'? + reserved 29; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // patternProperties, dependencies, propertyNames, const + reserved 30 to 33; + // Items in 'array' must be unique. + repeated string array = 34; + + enum JSONSchemaSimpleTypes { + UNKNOWN = 0; + ARRAY = 1; + BOOLEAN = 2; + INTEGER = 3; + NULL = 4; + NUMBER = 5; + OBJECT = 6; + STRING = 7; + } + + repeated JSONSchemaSimpleTypes type = 35; + // `Format` + string format = 36; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: contentMediaType, contentEncoding, if, then, else + reserved 37 to 41; + // field 42 is reserved for 'allOf', but in OpenAPI-specific way. + // TODO(ivucica): add 'allOf'? + reserved 42; + // following fields are reserved, as the properties have been omitted from + // OpenAPI v2: + // anyOf, oneOf, not + reserved 43 to 45; + // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1 + repeated string enum = 46; + + // Additional field level properties used when generating the OpenAPI v2 file. + FieldConfiguration field_configuration = 1001; + + // 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file. + // These properties are not defined by OpenAPIv2, but they are used to control the generation. + message FieldConfiguration { + // Alternative parameter name when used as path parameter. If set, this will + // be used as the complete parameter name when this field is used as a path + // parameter. Use this to avoid having auto generated path parameter names + // for overlapping paths. + string path_param_name = 47; + } + map extensions = 48; +} + +// `Tag` is a representation of OpenAPI v2 specification's Tag object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject +// +message Tag { + // field 1 is reserved for 'name'. In our generator, this is (to be) extracted + // from the name of proto service, and thus not exposed to the user, as + // changing tag object's name would break the link to the references to the + // tag in individual operation specifications. + // + // TODO(ivucica): Add 'name' property. Use it to allow override of the name of + // global Tag object, then use that name to reference the tag throughout the + // OpenAPI file. + reserved 1; + // A short description for the tag. GFM syntax can be used for rich text + // representation. + string description = 2; + // Additional external documentation for this tag. + ExternalDocumentation external_docs = 3; +} + +// `SecurityDefinitions` is a representation of OpenAPI v2 specification's +// Security Definitions object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject +// +// A declaration of the security schemes available to be used in the +// specification. This does not enforce the security schemes on the operations +// and only serves to provide the relevant details for each scheme. +message SecurityDefinitions { + // A single security scheme definition, mapping a "name" to the scheme it + // defines. + map security = 1; +} + +// `SecurityScheme` is a representation of OpenAPI v2 specification's +// Security Scheme object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject +// +// Allows the definition of a security scheme that can be used by the +// operations. Supported schemes are basic authentication, an API key (either as +// a header or as a query parameter) and OAuth2's common flows (implicit, +// password, application and access code). +message SecurityScheme { + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + enum Type { + TYPE_INVALID = 0; + TYPE_BASIC = 1; + TYPE_API_KEY = 2; + TYPE_OAUTH2 = 3; + } + + // The location of the API key. Valid values are "query" or "header". + enum In { + IN_INVALID = 0; + IN_QUERY = 1; + IN_HEADER = 2; + } + + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + enum Flow { + FLOW_INVALID = 0; + FLOW_IMPLICIT = 1; + FLOW_PASSWORD = 2; + FLOW_APPLICATION = 3; + FLOW_ACCESS_CODE = 4; + } + + // The type of the security scheme. Valid values are "basic", + // "apiKey" or "oauth2". + Type type = 1; + // A short description for security scheme. + string description = 2; + // The name of the header or query parameter to be used. + // Valid for apiKey. + string name = 3; + // The location of the API key. Valid values are "query" or + // "header". + // Valid for apiKey. + In in = 4; + // The flow used by the OAuth2 security scheme. Valid values are + // "implicit", "password", "application" or "accessCode". + // Valid for oauth2. + Flow flow = 5; + // The authorization URL to be used for this flow. This SHOULD be in + // the form of a URL. + // Valid for oauth2/implicit and oauth2/accessCode. + string authorization_url = 6; + // The token URL to be used for this flow. This SHOULD be in the + // form of a URL. + // Valid for oauth2/password, oauth2/application and oauth2/accessCode. + string token_url = 7; + // The available scopes for the OAuth2 security scheme. + // Valid for oauth2. + Scopes scopes = 8; + map extensions = 9; +} + +// `SecurityRequirement` is a representation of OpenAPI v2 specification's +// Security Requirement object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject +// +// Lists the required security schemes to execute this operation. The object can +// have multiple security schemes declared in it which are all required (that +// is, there is a logical AND between the schemes). +// +// The name used for each property MUST correspond to a security scheme +// declared in the Security Definitions. +message SecurityRequirement { + // If the security scheme is of type "oauth2", then the value is a list of + // scope names required for the execution. For other security scheme types, + // the array MUST be empty. + message SecurityRequirementValue { + repeated string scope = 1; + } + // Each name must correspond to a security scheme which is declared in + // the Security Definitions. If the security scheme is of type "oauth2", + // then the value is a list of scope names required for the execution. + // For other security scheme types, the array MUST be empty. + map security_requirement = 1; +} + +// `Scopes` is a representation of OpenAPI v2 specification's Scopes object. +// +// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject +// +// Lists the available scopes for an OAuth2 security scheme. +message Scopes { + // Maps between a name of a scope to a short description of it (as the value + // of the property). + map scope = 1; +} diff --git a/lora-ns-ttn/src/main/resources/application.properties b/lora-ns-ttn/src/main/resources/application.properties index 1efd78625..b2aa9a652 100644 --- a/lora-ns-ttn/src/main/resources/application.properties +++ b/lora-ns-ttn/src/main/resources/application.properties @@ -1 +1 @@ -application.name=lora-ns-orbiwise +application.name=lora-ns-ttn diff --git a/web/1014.0.172/devicemanagement-lora/app.module.ts b/web/1014.0.172/devicemanagement-lora/app.module.ts index ad4b42af4..bac9c7dbb 100644 --- a/web/1014.0.172/devicemanagement-lora/app.module.ts +++ b/web/1014.0.172/devicemanagement-lora/app.module.ts @@ -1,54 +1,65 @@ -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterModule as NgRouterModule } from '@angular/router'; -import { UpgradeModule as NgUpgradeModule } from '@angular/upgrade/static'; -import { CoreModule, HOOK_NAVIGATOR_NODES, HOOK_ROUTE, PluginsModule, RouterModule, ViewContext, DynamicFormsModule } from '@c8y/ngx-components'; -import { AssetsNavigatorModule } from '@c8y/ngx-components/assets-navigator'; -import { SubAssetsModule } from '@c8y/ngx-components/sub-assets'; -import { ChildDevicesModule } from '@c8y/ngx-components/child-devices'; -import { DeviceGridExampleModule } from '@c8y/ngx-components/device-grid-example'; -import { DeviceProfileModule } from '@c8y/ngx-components/device-profile'; -import { DeviceShellModule } from '@c8y/ngx-components/device-shell'; -import { OperationsModule } from '@c8y/ngx-components/operations'; -import { ImpactProtocolModule } from '@c8y/ngx-components/protocol-impact'; -import { OpcuaProtocolModule } from '@c8y/ngx-components/protocol-opcua'; -import { RepositoryModule } from '@c8y/ngx-components/repository'; -import { ServicesModule } from '@c8y/ngx-components/services'; -import { TrustedCertificatesModule } from '@c8y/ngx-components/trusted-certificates'; +import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { RouterModule as NgRouterModule } from "@angular/router"; +import { UpgradeModule as NgUpgradeModule } from "@angular/upgrade/static"; +import { + CoreModule, + HOOK_NAVIGATOR_NODES, + HOOK_ROUTE, + PluginsModule, + RouterModule, + ViewContext, + DynamicFormsModule, +} from "@c8y/ngx-components"; +import { AssetsNavigatorModule } from "@c8y/ngx-components/assets-navigator"; +import { SubAssetsModule } from "@c8y/ngx-components/sub-assets"; +import { ChildDevicesModule } from "@c8y/ngx-components/child-devices"; +import { DeviceGridExampleModule } from "@c8y/ngx-components/device-grid-example"; +import { DeviceProfileModule } from "@c8y/ngx-components/device-profile"; +import { DeviceShellModule } from "@c8y/ngx-components/device-shell"; +import { OperationsModule } from "@c8y/ngx-components/operations"; +import { ImpactProtocolModule } from "@c8y/ngx-components/protocol-impact"; +import { OpcuaProtocolModule } from "@c8y/ngx-components/protocol-opcua"; +import { RepositoryModule } from "@c8y/ngx-components/repository"; +import { ServicesModule } from "@c8y/ngx-components/services"; +import { TrustedCertificatesModule } from "@c8y/ngx-components/trusted-certificates"; import { DashboardUpgradeModule, HybridAppModule, UpgradeModule, - UPGRADE_ROUTES -} from '@c8y/ngx-components/upgrade'; -import { BinaryFileDownloadModule } from '@c8y/ngx-components/binary-file-download'; -import { SearchModule } from '@c8y/ngx-components/search'; -import { LpwanProtocolModule } from '@c8y/ngx-components/protocol-lpwan'; + UPGRADE_ROUTES, +} from "@c8y/ngx-components/upgrade"; +import { BinaryFileDownloadModule } from "@c8y/ngx-components/binary-file-download"; +import { SearchModule } from "@c8y/ngx-components/search"; +import { LpwanProtocolModule } from "@c8y/ngx-components/protocol-lpwan"; import { DeviceManagementHomeDashboardModule, - DeviceInfoDashboardModule -} from '@c8y/ngx-components/context-dashboard'; -import { DiagnosticsModule } from '@c8y/ngx-components/diagnostics'; + DeviceInfoDashboardModule, +} from "@c8y/ngx-components/context-dashboard"; +import { DiagnosticsModule } from "@c8y/ngx-components/diagnostics"; -import { FormlyModule } from '@ngx-formly/core'; -import { BsDatepickerModule } from 'ngx-bootstrap/datepicker'; -import { ModalModule } from 'ngx-bootstrap/modal'; -import { PopoverModule } from 'ngx-bootstrap/popover'; -import { TimepickerModule } from 'ngx-bootstrap/timepicker'; -import { MonacoEditorModule, NgxMonacoEditorConfig } from 'ngx-monaco-editor'; -import { LoraNavigationFactory } from './factories/Navigation'; -import { LoRaConfigComponent } from './src/config/config.component'; -import { DevicesComponent } from './src/devices/devices.component'; -import { LoraGuard } from './src/devices/lora.guard'; -import { PanelWrapperComponent } from './src/devices/panel-wrapper.component'; -import { GroupsComponent } from './src/groups/groups.component'; -import { LoraCodecsComponent } from './src/onboarding/codecs/codecs.component'; -import { RepeatTypeComponent } from './src/onboarding/codecs/repeat-section.type'; -import { LoraDevicesComponent, PropertyPipe } from './src/onboarding/devices/devices.component'; -import { LoraGatewaysComponent } from './src/onboarding/gateways/gateways.component'; -import { LNSEditComponent } from './src/onboarding/lns/lns-edit.component'; -import { LNSComponent } from './src/onboarding/lns/lns.component'; +import { FormlyModule } from "@ngx-formly/core"; +import { BsDatepickerModule } from "ngx-bootstrap/datepicker"; +import { ModalModule } from "ngx-bootstrap/modal"; +import { PopoverModule } from "ngx-bootstrap/popover"; +import { TimepickerModule } from "ngx-bootstrap/timepicker"; +import { MonacoEditorModule, NgxMonacoEditorConfig } from "ngx-monaco-editor"; +import { LoraNavigationFactory } from "./factories/Navigation"; +import { LoRaConfigComponent } from "./src/config/config.component"; +import { DevicesComponent } from "./src/devices/devices.component"; +import { LoraGuard } from "./src/devices/lora.guard"; +import { PanelWrapperComponent } from "./src/devices/panel-wrapper.component"; +import { GroupsComponent } from "./src/groups/groups.component"; +import { LoraCodecsComponent } from "./src/onboarding/codecs/codecs.component"; +import { RepeatTypeComponent } from "./src/onboarding/codecs/repeat-section.type"; +import { + LoraDevicesComponent, + PropertyPipe, +} from "./src/onboarding/devices/devices.component"; +import { LoraGatewaysComponent } from "./src/onboarding/gateways/gateways.component"; +import { LNSEditComponent } from "./src/onboarding/lns/lns-edit.component"; +import { LNSComponent } from "./src/onboarding/lns/lns.component"; const monacoConfig: NgxMonacoEditorConfig = { //baseUrl: 'app-name/assets', // configure base path for monaco editor default: './assets' @@ -77,7 +88,7 @@ class DownlinkData { payload: string; } `); -monaco.languages.typescript.javascriptDefaults.addExtraLib(`// Buffer class + monaco.languages.typescript.javascriptDefaults.addExtraLib(`// Buffer class type BufferEncoding = "ascii" | "utf8" | "utf16le" | "ucs2" | "base64" | "latin1" | "binary" | "hex"; interface Buffer extends Uint8Array { write(string: string, offset?: number, length?: number, encoding?: string): number; @@ -273,48 +284,64 @@ declare var Buffer: { poolSize: number; }; `); -} + }, }; @NgModule({ - declarations: [GroupsComponent, DevicesComponent, LoraDevicesComponent, LoraGatewaysComponent, LNSComponent, LNSEditComponent, LoraCodecsComponent, LoRaConfigComponent, PropertyPipe, RepeatTypeComponent, PanelWrapperComponent], + declarations: [ + GroupsComponent, + DevicesComponent, + LoraDevicesComponent, + LoraGatewaysComponent, + LNSComponent, + LNSEditComponent, + LoraCodecsComponent, + LoRaConfigComponent, + PropertyPipe, + RepeatTypeComponent, + PanelWrapperComponent, + ], entryComponents: [GroupsComponent, DevicesComponent], imports: [ // Upgrade module must be the first UpgradeModule, BrowserAnimationsModule, RouterModule.forRoot(), - NgRouterModule.forRoot([ - { - path: 'lora-device', - component: LoraDevicesComponent - }, - { - path: 'lora-gateway', - component: LoraGatewaysComponent - }, - { - path: 'lns', - component: LNSComponent - }, - { - path: 'lns/:lnsid', - component: LNSEditComponent - }, - { - path: 'codecs', - component: LoraCodecsComponent - }, - { - path: 'config', - component: LoRaConfigComponent - }, - { path: 'health', component: GroupsComponent }, - { path: 'lora_command', component: DevicesComponent }, - ...UPGRADE_ROUTES], { enableTracing: false, useHash: true }), + NgRouterModule.forRoot( + [ + { + path: "lora-device", + component: LoraDevicesComponent, + }, + { + path: "lora-gateway", + component: LoraGatewaysComponent, + }, + { + path: "lns", + component: LNSComponent, + }, + { + path: "lns/:lnsid", + component: LNSEditComponent, + }, + { + path: "codecs", + component: LoraCodecsComponent, + }, + { + path: "config", + component: LoRaConfigComponent, + }, + { path: "health", component: GroupsComponent }, + { path: "lora_command", component: DevicesComponent }, + ...UPGRADE_ROUTES, + ], + { enableTracing: false, useHash: true } + ), CoreModule.forRoot(), AssetsNavigatorModule.config({ - smartGroups: true + smartGroups: true, }), OperationsModule, OpcuaProtocolModule, @@ -343,45 +370,55 @@ declare var Buffer: { ReactiveFormsModule, DynamicFormsModule, FormlyModule.forRoot({ - types: [ - { name: 'repeat', component: RepeatTypeComponent }, - ], - wrappers: [ - { name: 'panel', component: PanelWrapperComponent }, - ], }) + types: [{ name: "repeat", component: RepeatTypeComponent }], + wrappers: [{ name: "panel", component: PanelWrapperComponent }], + }), ], providers: [ LoraGuard, - { provide: HOOK_NAVIGATOR_NODES, useClass: LoraNavigationFactory, multi: true }, { - provide: HOOK_ROUTE, // 1. - useValue: [{ // 2. - context: ViewContext.Group, // 3. - path: 'health', // 4. - component: GroupsComponent, // 5. - label: 'Health', // 6. - priority: 100, - icon: 'heart' - }], - multi: true - }, { - provide: HOOK_ROUTE, // 1. - useValue: [{ // 2. - context: ViewContext.Device, // 3. - path: 'lora_command', // 4. - component: DevicesComponent, // 5. - label: 'LoRa', // 6. - priority: 100, - icon: 'c8y-energy', - canActivate: [LoraGuard] - }], - multi: true - } - ] + provide: HOOK_NAVIGATOR_NODES, + useClass: LoraNavigationFactory, + multi: true, + }, + { + provide: HOOK_ROUTE, // 1. + useValue: [ + { + // 2. + context: ViewContext.Group, // 3. + path: "health", // 4. + component: GroupsComponent, // 5. + label: "Health", // 6. + priority: 100, + icon: "heart", + }, + ], + multi: true, + }, + { + provide: HOOK_ROUTE, // 1. + useValue: [ + { + // 2. + context: ViewContext.Device, // 3. + path: "lora_command", // 4. + component: DevicesComponent, // 5. + label: "LoRa", // 6. + priority: 100, + icon: "c8y-energy", + canActivate: [LoraGuard], + }, + ], + multi: true, + }, + ], }) export class AppModule extends HybridAppModule { constructor(protected upgrade: NgUpgradeModule) { super(); - window['c8y'].collections.properties['managed-objects'].properties.c8y_LpwanDevice.properties.provisioned.type = 'boolean'; + window["c8y"].collections.properties[ + "managed-objects" + ].properties.c8y_LpwanDevice.properties.provisioned.type = "boolean"; } } diff --git a/web/1014.0.172/devicemanagement-lora/extra-webpack.config.js b/web/1014.0.172/devicemanagement-lora/extra-webpack.config.js index 268f9bf92..5dc65a163 100644 --- a/web/1014.0.172/devicemanagement-lora/extra-webpack.config.js +++ b/web/1014.0.172/devicemanagement-lora/extra-webpack.config.js @@ -11,7 +11,7 @@ module.exports = function config(env) { patterns: [ { from: 'node_modules/monaco-editor', to: path.join(__dirname, './dist/apps/devicemanagement-lora/assets/monaco-editor') }, ], - }) + }, {debug: true}) ], } }; \ No newline at end of file diff --git a/web/1014.0.172/devicemanagement-lora/package-lock.json b/web/1014.0.172/devicemanagement-lora/package-lock.json index 2f7d2ab68..ec0da1f85 100644 --- a/web/1014.0.172/devicemanagement-lora/package-lock.json +++ b/web/1014.0.172/devicemanagement-lora/package-lock.json @@ -28,7 +28,7 @@ "angular": "1.6.9", "devextreme": "22.1.6", "devextreme-angular": "22.1.6", - "monaco-editor": "0.34.1", + "monaco-editor": "0.24.0", "ngx-monaco-editor": "12.0.0", "rxjs": "~6.6.3", "zone.js": "~0.11.4" @@ -42,7 +42,6 @@ "@c8y/cli": "1014.0.172", "@types/jest": "^27.0.3", "@types/node": "^18.11.9", - "@types/webpack": "^5.28.0", "file-loader": "^6.2.0", "jest": "^27.4.5", "jest-preset-angular": "^11.0.1", @@ -4971,17 +4970,6 @@ "node": ">=0.10.0" } }, - "node_modules/@types/webpack": { - "version": "5.28.0", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", - "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", - "dev": true, - "dependencies": { - "@types/node": "*", - "tapable": "^2.2.0", - "webpack": "^5" - } - }, "node_modules/@types/webpack-sources": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.9.tgz", @@ -9100,7 +9088,8 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/es-to-primitive": { "version": "1.2.1", @@ -16313,9 +16302,9 @@ } }, "node_modules/monaco-editor": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", - "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.24.0.tgz", + "integrity": "sha512-o1f0Lz6ABFNTtnEqqqvlY9qzNx24rQZx1RgYNQ8SkWkE+Ka63keHH/RqxQ4QhN4fs/UYOnvAtEUZsPrzccH++A==" }, "node_modules/move-concurrently": { "version": "1.0.1", @@ -23716,6 +23705,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -25000,6 +24990,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -25016,6 +25007,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -25024,19 +25016,22 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -25055,6 +25050,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "peer": true, "engines": { "node": ">=10.13.0" } diff --git a/web/1014.0.172/devicemanagement-lora/package.json b/web/1014.0.172/devicemanagement-lora/package.json index 83ddb8a65..fb4a112a8 100644 --- a/web/1014.0.172/devicemanagement-lora/package.json +++ b/web/1014.0.172/devicemanagement-lora/package.json @@ -32,7 +32,7 @@ "angular": "1.6.9", "devextreme": "22.1.6", "devextreme-angular": "22.1.6", - "monaco-editor": "0.34.1", + "monaco-editor": "0.24.0", "ngx-monaco-editor": "12.0.0", "rxjs": "~6.6.3", "zone.js": "~0.11.4" @@ -46,7 +46,6 @@ "@c8y/cli": "1014.0.172", "@types/jest": "^27.0.3", "@types/node": "^18.11.9", - "@types/webpack": "^5.28.0", "file-loader": "^6.2.0", "jest": "^27.4.5", "jest-preset-angular": "^11.0.1", @@ -66,4 +65,4 @@ }, "cli": {} } -} +} \ No newline at end of file