Request
Refactor RegisterApp to stop creating OpenZiti resources at registration time. Add a new EnrollApp RPC that apps call at startup to self-enroll: exchange their service_token for a fresh OpenZiti identity.
Specification
1. RegisterApp Refactor (internal/server/server.go)
Remove all ziti identity creation from RegisterApp:
- Remove the
CreateAppIdentity call and zitiResp variable
- Remove
cleanupZitiIdentity calls on error paths
- Set
ZitiIdentityID: "" and ZitiServiceID: "" in CreateAppInput
After refactoring, RegisterApp should:
- Validate slug/name
- Create platform identity (identity service)
- Generate + hash service token
- Write FGA tuple
- Create DB record with empty
ziti_identity_id and ziti_service_id
- Return
App + service_token
The cleanupZitiIdentity helper method is NOT removed — it's still used by DeleteApp and EnrollApp.
2. New EnrollApp Method (internal/server/server.go)
func (s *Server) EnrollApp(ctx context.Context, req *appsv1.EnrollAppRequest) (*appsv1.EnrollAppResponse, error) {
// 1. Validate: service_token must be non-empty → InvalidArgument
// 2. SHA-256 hash the raw token (same scheme as newServiceToken)
// 3. GetAppByServiceTokenHash → NotFound if no match
// 4. If app already has ziti resources: cleanupZitiIdentity (idempotent re-enrollment)
// 5. CreateAppIdentity via ziti-management → Internal on failure
// 6. UpdateAppZitiIdentity in DB → Internal on failure (cleanup ziti on DB error)
// 7. Return EnrollAppResponse{identity_json, identity_id}
}
Error codes:
| Failure |
gRPC Code |
Empty service_token |
InvalidArgument |
| App not found |
NotFound |
CreateAppIdentity fails |
Internal |
| DB update fails |
Internal |
Authentication: EnrollApp does NOT use identityFromMetadata. The app authenticates via the service_token in the request body (it has no platform identity header yet).
Idempotency: Safe to call on every pod restart. Old ziti identity/service are cleaned up, new ones created.
3. Store Changes (internal/store/store.go)
New method:
func (s *Store) UpdateAppZitiIdentity(ctx context.Context, id uuid.UUID, zitiIdentityID string, zitiServiceID string) error {
result, err := s.pool.Exec(ctx,
`UPDATE apps SET ziti_identity_id = $1, ziti_service_id = $2, updated_at = NOW() WHERE id = $3`,
zitiIdentityID,
zitiServiceID,
id,
)
if err != nil {
return err
}
if result.RowsAffected() == 0 {
return NotFound("app")
}
return nil
}
4. No Migration Needed
ziti_identity_id and ziti_service_id are already TEXT NOT NULL DEFAULT ''. Empty strings work as the "not yet enrolled" sentinel.
5. Gateway Impact
- Gateway
appproxy.go already handles empty ziti_service_id correctly (returns 502 "app service id missing")
- No changes needed in
appproxy.go
- The gateway's
apps.go will need a new EnrollApp proxy method (same pattern as existing RPCs) — but this is in agynio/gateway repo
Dependencies
- Requires
agynio/api issue #64 (proto change) to be merged and published to buf.build/agynio/api first
- After proto is published:
buf dep update + buf generate to regenerate
Request
Refactor
RegisterAppto stop creating OpenZiti resources at registration time. Add a newEnrollAppRPC that apps call at startup to self-enroll: exchange theirservice_tokenfor a fresh OpenZiti identity.Specification
1.
RegisterAppRefactor (internal/server/server.go)Remove all ziti identity creation from
RegisterApp:CreateAppIdentitycall andzitiRespvariablecleanupZitiIdentitycalls on error pathsZitiIdentityID: ""andZitiServiceID: ""inCreateAppInputAfter refactoring,
RegisterAppshould:ziti_identity_idandziti_service_idApp+service_tokenThe
cleanupZitiIdentityhelper method is NOT removed — it's still used byDeleteAppandEnrollApp.2. New
EnrollAppMethod (internal/server/server.go)Error codes:
service_tokenInvalidArgumentNotFoundCreateAppIdentityfailsInternalInternalAuthentication:
EnrollAppdoes NOT useidentityFromMetadata. The app authenticates via theservice_tokenin the request body (it has no platform identity header yet).Idempotency: Safe to call on every pod restart. Old ziti identity/service are cleaned up, new ones created.
3. Store Changes (
internal/store/store.go)New method:
4. No Migration Needed
ziti_identity_idandziti_service_idare alreadyTEXT NOT NULL DEFAULT ''. Empty strings work as the "not yet enrolled" sentinel.5. Gateway Impact
appproxy.goalready handles emptyziti_service_idcorrectly (returns 502 "app service id missing")appproxy.goapps.gowill need a newEnrollAppproxy method (same pattern as existing RPCs) — but this is inagynio/gatewayrepoDependencies
agynio/apiissue #64 (proto change) to be merged and published tobuf.build/agynio/apifirstbuf dep update+buf generateto regenerate