diff --git a/build/zitadel/generate-grpc.sh b/build/zitadel/generate-grpc.sh
index 771d12f261..47dd542d39 100755
--- a/build/zitadel/generate-grpc.sh
+++ b/build/zitadel/generate-grpc.sh
@@ -33,6 +33,20 @@ mkdir -p ${DOCS_PATH}
# generate additional output
+protoc \
+ -I=/proto/include \
+ --grpc-gateway_out ${GOPATH}/src \
+ --grpc-gateway_opt logtostderr=true \
+ --openapiv2_out ${OPENAPI_PATH} \
+ --openapiv2_opt logtostderr=true \
+ --authoption_out ${GRPC_PATH}/system \
+ --validate_out=lang=go:${GOPATH}/src \
+ ${PROTO_PATH}/system.proto
+
+# authoptions are generated into the wrong folder
+mv ${ZITADEL_PATH}/pkg/grpc/system/zitadel/* ${ZITADEL_PATH}/pkg/grpc/system
+rm -r ${ZITADEL_PATH}/pkg/grpc/system/zitadel
+
protoc \
-I=/proto/include \
--grpc-gateway_out ${GOPATH}/src \
@@ -80,6 +94,10 @@ mv ${ZITADEL_PATH}/pkg/grpc/auth/zitadel/* ${ZITADEL_PATH}/pkg/grpc/auth
rm -r ${ZITADEL_PATH}/pkg/grpc/auth/zitadel
## generate docs
+protoc \
+ -I=/proto/include \
+ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,system.md \
+ ${PROTO_PATH}/system.proto
protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,auth.md \
@@ -112,6 +130,10 @@ protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,idp.md \
${PROTO_PATH}/idp.proto
+protoc \
+ -I=/proto/include \
+ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,instance.md \
+ ${PROTO_PATH}/instance.proto
protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,member.md \
@@ -144,7 +166,11 @@ protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,project.md \
${PROTO_PATH}/project.proto
- protoc \
+protoc \
+ -I=/proto/include \
+ --doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,settings.md \
+ ${PROTO_PATH}/settings.proto
+protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,text.md \
${PROTO_PATH}/text.proto
@@ -158,4 +184,4 @@ protoc \
${PROTO_PATH}/settings.proto
-echo "done generating grpc"
\ No newline at end of file
+echo "done generating grpc"
diff --git a/docs/docs/apis/proto/instance.md b/docs/docs/apis/proto/instance.md
new file mode 100644
index 0000000000..69d7cda721
--- /dev/null
+++ b/docs/docs/apis/proto/instance.md
@@ -0,0 +1,108 @@
+---
+title: zitadel/instance.proto
+---
+> This document reflects the state from API 1.0 (available from 20.04.2021)
+
+
+
+
+## Messages
+
+
+### DomainsQuery
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| domains | repeated string | - | string.max_len: 200
|
+| method | zitadel.v1.ListQueryMethod | - | enum.defined_only: true
|
+
+
+
+
+### IdQuery
+IdQuery is always equals
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.max_len: 200
|
+
+
+
+
+### Instance
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | |
+| details | zitadel.v1.ObjectDetails | - | |
+| state | State | - | |
+| generated_domain | string | - | |
+| custom_domains | repeated string | - | |
+| name | string | - | |
+| version | string | - | |
+
+
+
+
+### Query
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.id_query | IdQuery | - | |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.domains_query | DomainsQuery | - | |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.state_query | StateQuery | - | |
+
+
+
+
+### StateQuery
+StateQuery is always equals
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| state | State | - | enum.defined_only: true
|
+
+
+
+
+
+
+## Enums
+
+
+### FieldName {#fieldname}
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| FIELD_NAME_UNSPECIFIED | 0 | - |
+| FIELD_NAME_ID | 1 | - |
+| FIELD_NAME_GENERATED_DOMAIN | 2 | - |
+| FIELD_NAME_NAME | 3 | - |
+| FIELD_NAME_CREATION_DATE | 4 | - |
+
+
+
+
+### State {#state}
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| STATE_UNSPECIFIED | 0 | - |
+| STATE_CREATING | 1 | - |
+| STATE_RUNNING | 2 | - |
+| STATE_STOPPING | 3 | - |
+| STATE_STOPPED | 4 | - |
+
+
+
+
diff --git a/docs/docs/apis/proto/object.md b/docs/docs/apis/proto/object.md
index a99399b475..1f925fe375 100644
--- a/docs/docs/apis/proto/object.md
+++ b/docs/docs/apis/proto/object.md
@@ -66,6 +66,16 @@ on manipulation: the | |
## Enums
+### ListQueryMethod {#listquerymethod}
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| LIST_QUERY_METHOD_IN | 0 | - |
+
+
+
+
### TextQueryMethod {#textquerymethod}
diff --git a/docs/docs/apis/proto/settings.md b/docs/docs/apis/proto/settings.md
new file mode 100644
index 0000000000..393118c453
--- /dev/null
+++ b/docs/docs/apis/proto/settings.md
@@ -0,0 +1,153 @@
+---
+title: zitadel/settings.proto
+---
+> This document reflects the state from API 1.0 (available from 20.04.2021)
+
+
+
+
+## Messages
+
+
+### DebugNotificationProvider
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| compact | bool | - | |
+
+
+
+
+### OIDCSettings
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| access_token_lifetime | google.protobuf.Duration | - | |
+| id_token_lifetime | google.protobuf.Duration | - | |
+| refresh_token_idle_expiration | google.protobuf.Duration | - | |
+| refresh_token_expiration | google.protobuf.Duration | - | |
+
+
+
+
+### SMSProvider
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| id | string | - | |
+| state | SMSProviderConfigState | - | |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.twilio | TwilioConfig | - | |
+
+
+
+
+### SMTPConfig
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| sender_address | string | - | |
+| sender_name | string | - | |
+| tls | bool | - | |
+| host | string | - | |
+| user | string | - | |
+
+
+
+
+### SecretGenerator
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| generator_type | SecretGeneratorType | - | |
+| details | zitadel.v1.ObjectDetails | - | |
+| length | uint32 | - | |
+| expiry | google.protobuf.Duration | - | |
+| include_lower_letters | bool | - | |
+| include_upper_letters | bool | - | |
+| include_digits | bool | - | |
+| include_symbols | bool | - | |
+
+
+
+
+### SecretGeneratorQuery
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.type_query | SecretGeneratorTypeQuery | - | |
+
+
+
+
+### SecretGeneratorTypeQuery
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| generator_type | SecretGeneratorType | - | |
+
+
+
+
+### TwilioConfig
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| sid | string | - | |
+| sender_number | string | - | |
+
+
+
+
+
+
+## Enums
+
+
+### SMSProviderConfigState {#smsproviderconfigstate}
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| SMS_PROVIDER_CONFIG_STATE_UNSPECIFIED | 0 | - |
+| SMS_PROVIDER_CONFIG_ACTIVE | 1 | - |
+| SMS_PROVIDER_CONFIG_INACTIVE | 2 | - |
+
+
+
+
+### SecretGeneratorType {#secretgeneratortype}
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| SECRET_GENERATOR_TYPE_UNSPECIFIED | 0 | - |
+| SECRET_GENERATOR_TYPE_INIT_CODE | 1 | - |
+| SECRET_GENERATOR_TYPE_VERIFY_EMAIL_CODE | 2 | - |
+| SECRET_GENERATOR_TYPE_VERIFY_PHONE_CODE | 3 | - |
+| SECRET_GENERATOR_TYPE_PASSWORD_RESET_CODE | 4 | - |
+| SECRET_GENERATOR_TYPE_PASSWORDLESS_INIT_CODE | 5 | - |
+| SECRET_GENERATOR_TYPE_APP_SECRET | 6 | - |
+
+
+
+
diff --git a/docs/docs/apis/proto/system.md b/docs/docs/apis/proto/system.md
new file mode 100644
index 0000000000..1b601750ef
--- /dev/null
+++ b/docs/docs/apis/proto/system.md
@@ -0,0 +1,522 @@
+---
+title: zitadel/system.proto
+---
+> This document reflects the state from API 1.0 (available from 20.04.2021)
+
+
+## SystemService {#zitadelsystemv1systemservice}
+
+
+### Healthz
+
+> **rpc** Healthz([HealthzRequest](#healthzrequest))
+[HealthzResponse](#healthzresponse)
+
+Indicates if ZITADEL is running.
+It respondes as soon as ZITADEL started
+
+
+
+ GET: /healthz
+
+
+### ListInstances
+
+> **rpc** ListInstances([ListInstancesRequest](#listinstancesrequest))
+[ListInstancesResponse](#listinstancesresponse)
+
+Returns a list of ZITADEL instances/tenants
+
+
+
+ POST: /instances
+
+
+### GetInstance
+
+> **rpc** GetInstance([GetInstanceRequest](#getinstancerequest))
+[GetInstanceResponse](#getinstanceresponse)
+
+Returns the detail of an instance
+
+
+
+ GET: /instances/{id}
+
+
+### AddInstance
+
+> **rpc** AddInstance([AddInstanceRequest](#addinstancerequest))
+[AddInstanceResponse](#addinstanceresponse)
+
+Creates a new instance with all needed setup data
+This might take some time
+
+
+
+ POST: /instances
+
+
+### RemoveInstance
+
+> **rpc** RemoveInstance([RemoveInstanceRequest](#removeinstancerequest))
+[RemoveInstanceResponse](#removeinstanceresponse)
+
+Removes a instances
+This might take some time
+
+
+
+ DELETE: /instances/{id}
+
+
+### GetUsage
+
+> **rpc** GetUsage([GetUsageRequest](#getusagerequest))
+[GetUsageResponse](#getusageresponse)
+
+Returns the usage metrics of an instance
+
+
+
+ GET: /instances/{id}/usage
+
+
+### GetGeneratedDomain
+
+> **rpc** GetGeneratedDomain([GetGeneratedDomainRequest](#getgenerateddomainrequest))
+[GetGeneratedDomainResponse](#getgenerateddomainresponse)
+
+Returns the domain of an instance
+
+
+
+ GET: /instances/{id}/domains/generated
+
+
+### GetCustomDomains
+
+> **rpc** GetCustomDomains([GetCustomDomainsRequest](#getcustomdomainsrequest))
+[GetCustomDomainsResponse](#getcustomdomainsresponse)
+
+Returns the custom domains of an instance
+
+
+
+ GET: /instances/{id}/domains/custom
+
+
+### AddCustomDomain
+
+> **rpc** AddCustomDomain([AddCustomDomainRequest](#addcustomdomainrequest))
+[AddCustomDomainResponse](#addcustomdomainresponse)
+
+Returns the domain of an instance
+
+
+
+ POST: /instances/{id}/domains/custom
+
+
+### ListViews
+
+> **rpc** ListViews([ListViewsRequest](#listviewsrequest))
+[ListViewsResponse](#listviewsresponse)
+
+Returns all stored read models of ZITADEL
+views are used for search optimisation and optimise request latencies
+they represent the delta of the event happend on the objects
+
+
+
+ POST: /views/_search
+
+
+### ClearView
+
+> **rpc** ClearView([ClearViewRequest](#clearviewrequest))
+[ClearViewResponse](#clearviewresponse)
+
+Truncates the delta of the change stream
+be carefull with this function because ZITADEL has to
+recompute the deltas after they got cleared.
+Search requests will return wrong results until all deltas are recomputed
+
+
+
+ POST: /views/{database}/{view_name}
+
+
+### ListFailedEvents
+
+> **rpc** ListFailedEvents([ListFailedEventsRequest](#listfailedeventsrequest))
+[ListFailedEventsResponse](#listfailedeventsresponse)
+
+Returns event descriptions which cannot be processed.
+It's possible that some events need some retries.
+For example if the SMTP-API wasn't able to send an email at the first time
+
+
+
+ POST: /failedevents/_search
+
+
+### RemoveFailedEvent
+
+> **rpc** RemoveFailedEvent([RemoveFailedEventRequest](#removefailedeventrequest))
+[RemoveFailedEventResponse](#removefailedeventresponse)
+
+Deletes the event from failed events view.
+the event is not removed from the change stream
+This call is usefull if the system was able to process the event later.
+e.g. if the second try of sending an email was successful. the first try produced a
+failed event. You can find out if it worked on the `failure_count`
+
+
+
+ DELETE: /failedevents/{database}/{view_name}/{failed_sequence}
+
+
+
+
+
+
+
+## Messages
+
+
+### AddCustomDomainRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+| custom_domain | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### AddCustomDomainResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
+### AddInstanceRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| instance_name | string | - | string.min_len: 1
string.max_len: 200
|
+| first_org_name | string | - | string.min_len: 1
string.max_len: 200
|
+| custom_domain | string | - | string.max_len: 200
|
+| owner_first_name | string | - | string.min_len: 1
string.max_len: 200
|
+| owner_last_name | string | - | string.min_len: 1
string.max_len: 200
|
+| owner_email | string | - | string.min_len: 1
string.max_len: 200
|
+| owner_username | string | - | string.min_len: 1
string.max_len: 200
|
+| password | string | - | string.min_len: 1
string.max_len: 200
|
+| request_limit | uint64 | - | |
+| action_mins_limit | uint64 | - | |
+
+
+
+
+### AddInstanceResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | |
+
+
+
+
+### ChangeSubscriptionRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| domain | string | - | string.min_len: 1
string.max_len: 200
|
+| subscription_name | string | - | string.min_len: 1
string.max_len: 200
|
+| request_limit | uint64 | - | |
+| action_mins_limit | uint64 | - | |
+
+
+
+
+### ChangeSubscriptionResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
+### ClearViewRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| database | string | - | string.min_len: 1
string.max_len: 200
|
+| view_name | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### ClearViewResponse
+This is an empty response
+
+
+
+
+### FailedEvent
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| database | string | - | |
+| view_name | string | - | |
+| failed_sequence | uint64 | - | |
+| failure_count | uint64 | - | |
+| error_message | string | - | |
+
+
+
+
+### GetCustomDomainsRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### GetCustomDomainsResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| domains | repeated string | - | |
+
+
+
+
+### GetGeneratedDomainRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### GetGeneratedDomainResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| domain | string | - | |
+
+
+
+
+### GetInstanceRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### GetInstanceResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| instance | zitadel.instance.v1.Instance | - | |
+
+
+
+
+### GetUsageRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### GetUsageResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+| executed_requests | uint64 | - | |
+| executed_action_mins | uint64 | - | |
+
+
+
+
+### HealthzRequest
+This is an empty request
+
+
+
+
+### HealthzResponse
+This is an empty response
+
+
+
+
+### ListFailedEventsRequest
+This is an empty request
+
+
+
+
+### ListFailedEventsResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| result | repeated FailedEvent | TODO: list details | |
+
+
+
+
+### ListInstancesRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| query | zitadel.v1.ListQuery | list limitations and ordering | |
+| sorting_column | zitadel.instance.v1.FieldName | the field the result is sorted | |
+| queries | repeated zitadel.instance.v1.Query | criterias the client is looking for | |
+
+
+
+
+### ListInstancesResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ListDetails | - | |
+| sorting_column | zitadel.instance.v1.FieldName | - | |
+| result | repeated zitadel.instance.v1.Instance | - | |
+
+
+
+
+### ListViewsRequest
+This is an empty request
+
+
+
+
+### ListViewsResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| result | repeated View | TODO: list details | |
+
+
+
+
+### RemoveFailedEventRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| database | string | - | string.min_len: 1
string.max_len: 200
|
+| view_name | string | - | string.min_len: 1
string.max_len: 200
|
+| failed_sequence | uint64 | - | |
+
+
+
+
+### RemoveFailedEventResponse
+This is an empty response
+
+
+
+
+### RemoveInstanceRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| id | string | - | string.min_len: 1
string.max_len: 200
|
+
+
+
+
+### RemoveInstanceResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
+### View
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| database | string | - | |
+| view_name | string | - | |
+| processed_sequence | uint64 | - | |
+| event_timestamp | google.protobuf.Timestamp | The timestamp the event occured | |
+| last_successful_spooler_run | google.protobuf.Timestamp | - | |
+| instance | string | - | |
+
+
+
+
+
+
diff --git a/proto/zitadel/instance.proto b/proto/zitadel/instance.proto
new file mode 100644
index 0000000000..f33a62841a
--- /dev/null
+++ b/proto/zitadel/instance.proto
@@ -0,0 +1,104 @@
+syntax = "proto3";
+
+import "zitadel/object.proto";
+import "validate/validate.proto";
+import "protoc-gen-openapiv2/options/annotations.proto";
+
+package zitadel.instance.v1;
+
+option go_package ="github.com/caos/zitadel/pkg/grpc/instance";
+
+message Instance {
+ string id = 1 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"69629023906488334\""
+ }
+ ];
+ zitadel.v1.ObjectDetails details = 2;
+ State state = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "current state of the instance";
+ }
+ ];
+ string generated_domain = 4 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"organization.zitadel.com\"";
+ }
+ ];
+ repeated string custom_domains = 5 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "[\"zitadel.com\", \"zitadel.cloud\", \"zitadel.ch\"]";
+ }
+ ];
+ string name = 6 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"ZITADEL\"";
+ }
+ ];
+ string version = 7 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"v1.0.0\"";
+ }
+ ];
+}
+
+enum State {
+ STATE_UNSPECIFIED = 0;
+ STATE_CREATING = 1;
+ STATE_RUNNING = 2;
+ STATE_STOPPING = 3;
+ STATE_STOPPED = 4;
+}
+
+message Query {
+ oneof query {
+ option (validate.required) = true;
+
+ IdQuery id_query = 1;
+ DomainsQuery domains_query = 2;
+ StateQuery state_query = 3;
+ }
+}
+
+//IdQuery is always equals
+message IdQuery {
+ string id = 1 [
+ (validate.rules).string = {max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "4820840938402429";
+ }
+ ];
+}
+
+message DomainsQuery {
+ repeated string domains = 1 [
+ (validate.rules).string = {max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"caos.ch\"";
+ }
+ ];
+ zitadel.v1.ListQueryMethod method = 2 [
+ (validate.rules).enum.defined_only = true,
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "defines which list equality method is used";
+ }
+ ];
+}
+
+//StateQuery is always equals
+message StateQuery {
+ State state = 1 [
+ (validate.rules).enum.defined_only = true,
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "current state of the instance";
+ }
+ ];
+}
+
+enum FieldName {
+ FIELD_NAME_UNSPECIFIED = 0;
+ FIELD_NAME_ID = 1;
+ FIELD_NAME_GENERATED_DOMAIN = 2;
+ FIELD_NAME_NAME = 3;
+ FIELD_NAME_CREATION_DATE = 4;
+}
diff --git a/proto/zitadel/object.proto b/proto/zitadel/object.proto
index c709b58d33..0a883bee3b 100644
--- a/proto/zitadel/object.proto
+++ b/proto/zitadel/object.proto
@@ -85,3 +85,8 @@ enum TextQueryMethod {
TEXT_QUERY_METHOD_ENDS_WITH = 6;
TEXT_QUERY_METHOD_ENDS_WITH_IGNORE_CASE = 7;
}
+
+
+enum ListQueryMethod {
+ LIST_QUERY_METHOD_IN = 0;
+}
diff --git a/proto/zitadel/system.proto b/proto/zitadel/system.proto
new file mode 100644
index 0000000000..e10a3ea0ba
--- /dev/null
+++ b/proto/zitadel/system.proto
@@ -0,0 +1,524 @@
+syntax = "proto3";
+
+import "zitadel/object.proto";
+import "zitadel/options.proto";
+import "zitadel/instance.proto";
+import "zitadel/text.proto";
+
+import "google/api/annotations.proto";
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/duration.proto";
+
+import "protoc-gen-openapiv2/options/annotations.proto";
+
+import "validate/validate.proto";
+
+package zitadel.system.v1;
+
+option go_package ="github.com/caos/zitadel/pkg/grpc/system";
+
+option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+ info: {
+ title: "System API";
+ version: "1.0";
+ description: "This API is intended to configure and manage the different tenants whithin ZITADEL.";
+ contact:{
+ name: "CAOS developers of ZITADEL"
+ url: "https://zitadel.ch"
+ email: "hi@zitadel.ch"
+ }
+ license: {
+ name: "Apache 2.0",
+ url: "https://github.com/caos/zitadel/blob/main/LICENSE";
+ };
+ };
+
+ schemes: HTTPS;
+ schemes: HTTP;
+
+ consumes: "application/json";
+ consumes: "application/grpc";
+
+ produces: "application/json";
+ produces: "application/grpc";
+
+ consumes: "application/grpc-web+proto";
+ produces: "application/grpc-web+proto";
+
+ host: "api.zitadel.ch";
+ base_path: "/system/v1";
+
+ external_docs: {
+ description: "Detailed information about ZITADEL",
+ url: "https://docs.zitadel.ch"
+ }
+
+ responses: {
+ key: "403";
+ value: {
+ description: "Returned when the user does not have permission to access the resource.";
+ schema: {
+ json_schema: {
+ ref: "#/definitions/rpcStatus";
+ }
+ }
+ }
+ }
+ responses: {
+ key: "404";
+ value: {
+ description: "Returned when the resource does not exist.";
+ schema: {
+ json_schema: {
+ ref: "#/definitions/rpcStatus";
+ }
+ }
+ }
+ }
+};
+
+service SystemService {
+ //Indicates if ZITADEL is running.
+ // It respondes as soon as ZITADEL started
+ rpc Healthz(HealthzRequest) returns (HealthzResponse) {
+ option (google.api.http) = {
+ get: "/healthz";
+ };
+
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "probes";
+ responses: {
+ key: "200";
+ value: {
+ description: "ZITADEL started";
+ };
+ }
+ responses: {
+ key: "default";
+ value: {
+ description: "ZITADEL NOT started yet";
+ };
+ }
+ };
+ }
+
+ // Returns a list of ZITADEL instances/tenants
+ rpc ListInstances(ListInstancesRequest) returns (ListInstancesResponse) {
+ option (google.api.http) = {
+ post: "/instances"
+ body: "*"
+ };
+ }
+
+ // Returns the detail of an instance
+ rpc GetInstance(GetInstanceRequest) returns (GetInstanceResponse) {
+ option (google.api.http) = {
+ get: "/instances/{id}";
+ };
+ }
+
+ // Creates a new instance with all needed setup data
+ // This might take some time
+ rpc AddInstance(AddInstanceRequest) returns (AddInstanceResponse) {
+ option (google.api.http) = {
+ post: "/instances"
+ body: "*"
+ };
+ }
+
+ // Removes a instances
+ // This might take some time
+ rpc RemoveInstance(RemoveInstanceRequest) returns (RemoveInstanceResponse) {
+ option (google.api.http) = {
+ delete: "/instances/{id}"
+ };
+ }
+
+ // Returns the usage metrics of an instance
+ rpc GetUsage(GetUsageRequest) returns (GetUsageResponse) {
+ option (google.api.http) = {
+ get: "/instances/{id}/usage";
+ };
+ }
+
+ // Returns the domain of an instance
+ rpc GetGeneratedDomain(GetGeneratedDomainRequest) returns (GetGeneratedDomainResponse) {
+ option (google.api.http) = {
+ get: "/instances/{id}/domains/generated";
+ };
+ }
+
+ // Returns the custom domains of an instance
+ rpc GetCustomDomains(GetCustomDomainsRequest) returns (GetCustomDomainsResponse) {
+ option (google.api.http) = {
+ get: "/instances/{id}/domains/custom";
+ };
+ }
+
+ // Returns the domain of an instance
+ rpc AddCustomDomain(AddCustomDomainRequest) returns (AddCustomDomainResponse) {
+ option (google.api.http) = {
+ post: "/instances/{id}/domains/custom";
+ body: "*"
+ };
+ }
+
+ //Returns all stored read models of ZITADEL
+ // views are used for search optimisation and optimise request latencies
+ // they represent the delta of the event happend on the objects
+ rpc ListViews(ListViewsRequest) returns (ListViewsResponse) {
+ option (google.api.http) = {
+ post: "/views/_search";
+ };
+
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "views";
+ external_docs: {
+ url: "https://docs.zitadel.ch/concepts#Software_Architecture";
+ description: "details of ZITADEL's event driven software concepts";
+ };
+ responses: {
+ key: "200";
+ value: {
+ description: "Views for query operations";
+ };
+ };
+ };
+ }
+
+ //Truncates the delta of the change stream
+ // be carefull with this function because ZITADEL has to
+ // recompute the deltas after they got cleared.
+ // Search requests will return wrong results until all deltas are recomputed
+ rpc ClearView(ClearViewRequest) returns (ClearViewResponse) {
+ option (google.api.http) = {
+ post: "/views/{database}/{view_name}";
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "views";
+ external_docs: {
+ url: "https://docs.zitadel.ch/concepts#Software_Architecture";
+ description: "details of ZITADEL's event driven software concepts";
+ };
+ responses: {
+ key: "200";
+ value: {
+ description: "View cleared";
+ };
+ };
+ };
+ }
+
+ //Returns event descriptions which cannot be processed.
+ // It's possible that some events need some retries.
+ // For example if the SMTP-API wasn't able to send an email at the first time
+ rpc ListFailedEvents(ListFailedEventsRequest) returns (ListFailedEventsResponse) {
+ option (google.api.http) = {
+ post: "/failedevents/_search";
+ };
+
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "failed events";
+ external_docs: {
+ url: "https://docs.zitadel.ch/concepts#Software_Architecture";
+ description: "details of ZITADEL's event driven software concepts";
+ };
+ responses: {
+ key: "200";
+ value: {
+ description: "Events which were not processed by the views";
+ };
+ };
+ };
+ }
+
+ //Deletes the event from failed events view.
+ // the event is not removed from the change stream
+ // This call is usefull if the system was able to process the event later.
+ // e.g. if the second try of sending an email was successful. the first try produced a
+ // failed event. You can find out if it worked on the `failure_count`
+ rpc RemoveFailedEvent(RemoveFailedEventRequest) returns (RemoveFailedEventResponse) {
+ option (google.api.http) = {
+ delete: "/failedevents/{database}/{view_name}/{failed_sequence}";
+ };
+
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: "failed events";
+ external_docs: {
+ url: "https://docs.zitadel.ch/concepts#Software_Architecture";
+ description: "details of ZITADEL's event driven software concepts";
+ };
+ responses: {
+ key: "200";
+ value: {
+ description: "Events removed from the list";
+ };
+ };
+ responses: {
+ key: "400";
+ value: {
+ description: "failed event not found";
+ schema: {
+ json_schema: {
+ ref: "#/definitions/rpcStatus";
+ };
+ };
+ };
+ };
+ };
+ }
+}
+
+
+//This is an empty request
+message HealthzRequest {}
+
+//This is an empty response
+message HealthzResponse {}
+
+message ListInstancesRequest {
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+ json_schema: {
+ description: "Search query for lists";
+ required: ["query"]
+ };
+ };
+
+ //list limitations and ordering
+ zitadel.v1.ListQuery query = 1;
+ // the field the result is sorted
+ zitadel.instance.v1.FieldName sorting_column = 2;
+ //criterias the client is looking for
+ repeated zitadel.instance.v1.Query queries = 3;
+}
+
+message ListInstancesResponse {
+ zitadel.v1.ListDetails details = 1;
+ zitadel.instance.v1.FieldName sorting_column = 2;
+ repeated zitadel.instance.v1.Instance result = 3;
+}
+
+message GetInstanceRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message GetInstanceResponse {
+ zitadel.instance.v1.Instance instance = 1;
+}
+
+message AddInstanceRequest {
+ string instance_name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string first_org_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string custom_domain = 3 [(validate.rules).string = {max_len: 200}];
+ string owner_first_name = 4 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string owner_last_name = 5 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string owner_email = 6 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string owner_username = 7 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string password = 8 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ uint64 request_limit = 9;
+ uint64 action_mins_limit = 10;
+}
+
+message AddInstanceResponse {
+ string id = 1;
+}
+
+message RemoveInstanceRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message RemoveInstanceResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+message GetUsageRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message GetUsageResponse {
+ zitadel.v1.ObjectDetails details = 1;
+ uint64 executed_requests = 2;
+ uint64 executed_action_mins = 3;
+}
+
+message GetGeneratedDomainRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message GetGeneratedDomainResponse {
+ zitadel.v1.ObjectDetails details = 1;
+ string domain = 2;
+}
+
+message GetCustomDomainsRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message GetCustomDomainsResponse {
+ zitadel.v1.ObjectDetails details = 1;
+ repeated string domains = 2;
+}
+
+message AddCustomDomainRequest {
+ string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string custom_domain = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
+}
+
+message AddCustomDomainResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+message ChangeSubscriptionRequest {
+ string domain = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string subscription_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ uint64 request_limit = 3;
+ uint64 action_mins_limit = 4;
+}
+
+message ChangeSubscriptionResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+//This is an empty request
+message ListViewsRequest {}
+
+message ListViewsResponse {
+ //TODO: list details
+ repeated View result = 1;
+}
+
+message ClearViewRequest {
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+ json_schema: {
+ required: ["database", "view_name"]
+ };
+ };
+
+ string database = 1 [
+ (validate.rules).string = {min_len: 1, max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"adminapi\"";
+ min_length: 1;
+ max_length: 200;
+ }
+ ];
+ string view_name = 2 [
+ (validate.rules).string = {min_len: 1, max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"iam_members\"";
+ min_length: 1;
+ max_length: 200;
+ }
+ ];
+}
+
+//This is an empty response
+message ClearViewResponse {}
+
+//This is an empty request
+message ListFailedEventsRequest {}
+
+message ListFailedEventsResponse {
+ //TODO: list details
+ repeated FailedEvent result = 1;
+}
+
+message RemoveFailedEventRequest {
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+ json_schema: {
+ required: ["database", "view_name", "failed_sequence"]
+ };
+ };
+
+ string database = 1 [
+ (validate.rules).string = {min_len: 1, max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"adminapi\"";
+ min_length: 1;
+ max_length: 200;
+ }
+ ];
+ string view_name = 2 [
+ (validate.rules).string = {min_len: 1, max_len: 200},
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"iam_members\"";
+ min_length: 1;
+ max_length: 200;
+ }
+ ];
+ uint64 failed_sequence = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"9823758\"";
+ }
+ ];
+}
+
+//This is an empty response
+message RemoveFailedEventResponse {}
+
+message View {
+ string database = 1 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"adminapi\"";
+ }
+ ];
+ string view_name = 2 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"iam_members\"";
+ }
+ ];
+ uint64 processed_sequence = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"9823758\"";
+ }
+ ];
+ google.protobuf.Timestamp event_timestamp = 4 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"2019-04-01T08:45:00.000000Z\"";
+ description: "The timestamp the event occured";
+ }
+ ]; // The timestamp the event occured
+ google.protobuf.Timestamp last_successful_spooler_run = 5 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ description: "The timestamp the event occured";
+ }
+ ];
+ string instance = 6 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"840498034930840\"";
+ }
+ ];
+}
+
+message FailedEvent {
+ string database = 1 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"adminapi\"";
+ }
+ ];
+ string view_name = 2 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"iam_members\"";
+ }
+ ];
+ uint64 failed_sequence = 3 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"9823759\"";
+ }
+ ];
+ uint64 failure_count = 4 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"5\"";
+ }
+ ];
+ string error_message = 5 [
+ (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+ example: "\"ID=EXAMP-ID3ER Message=Example message\"";
+ }
+ ];
+}