How Does the Authorization Module Work with Akash's Custom Modules?

Akash's custom modules integrate with the Cosmos SDK x/authz module through a thin AuthzKeeper wrapper interface that delegates scoped spending permissions via custom DepositAuthorization grants, enabling automated deposit validation, consumption during payments, and cleanup on account closure.

The akash-network/node repository implements a sophisticated authorization pattern that allows the Escrow, Deployment, and Market modules to leverage Cosmos SDK's granular permission system without direct dependencies on the underlying x/authz keeper. This architecture uses a custom authorization type to manage deposit flows while maintaining clean module boundaries.

Architectural Overview of Authz Integration

Akash's authorization architecture centers on a wrapper interface that isolates custom modules from the Cosmos SDK's concrete implementation. This design enables the system to grant time-bound, spend-limited permissions that are automatically validated during deployment creation and consumed during payment processing.

The AuthzKeeper Interface Abstraction

The AuthzKeeper interface defined in x/escrow/keeper/external.go provides the contract between Akash's custom modules and the Cosmos authorization system:

type AuthzKeeper interface {
    DeleteGrant(ctx context.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) error
    GetAuthorization(ctx context.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (authz.Authorization, *time.Time)
    SaveGrant(ctx context.Context, grantee sdk.AccAddress, granter sdk.AccAddress, authorization authz.Authorization, expiration *time.Time) error
    IterateGrants(ctx context.Context, handler func(granterAddr sdk.AccAddress, granteeAddr sdk.AccAddress, grant authz.Grant) bool)
    GetGranteeGrantsByMsgType(ctx context.Context, grantee sdk.AccAddress, msgType string, onGrant authzkeeper.OnGrantFn)
}

This abstraction allows the Escrow module to remain agnostic of the concrete x/authz keeper implementation while supporting mock implementations for unit testing.

Custom DepositAuthorization Grant Type

The system utilizes a custom authorization type located in pkg/akt.dev/go/node/escrow/v1. The DepositAuthorization implements the authz.Authorization interface and carries:

  • A spend limit (sdk.Coins) defining the maximum transferable amount
  • A scope identifier (DepositScopeDeployment, DepositScopeMarket, etc.) restricting usage to specific module contexts
  • The TryAccept method that validates and consumes partial grants during payment processing

Authorization Flow Across Custom Modules

The integration follows a distinct lifecycle: grants are created during deployment initialization, consumed incrementally during payment withdrawals, and revoked when resources are decommissioned.

Step 1: Deployment Creation and Deposit Authorization

When a user creates a deployment via CreateDeployment in x/deployment/handler/server.go, the system delegates deposit validation to the escrow module:

// x/deployment/handler/server.go – CreateDeployment handler
deposits, err := ms.escrow.AuthorizeDeposits(ctx, msg) // authz validation occurs here
if err != nil {
    return nil, err
}
...
if err := ms.escrow.AccountCreate(ctx, deployment.ID.ToEscrowAccountID(), owner, deposits); err != nil {
    return &types.MsgCreateDeploymentResponse{}, err
}

The AuthorizeDeposits function in x/escrow/keeper/keeper.go examines each deposit source. For SourceBalance, it verifies the owner's spendable coins. For SourceAuthz, it queries existing grants via GetAuthorization and may create new grants via SaveGrant:

// x/escrow/keeper/keeper.go – AuthorizeDeposits logic
for _, source := range dep.Sources {
    switch source {
    case deposit.SourceBalance:
        // Verify direct spendable balance
    case deposit.SourceAuthz:
        // Fetch or create authz grant
        authz, _ := k.authzKeeper.GetAuthorization(sctx, owner, granter, ev1.MsgTypeDeposit)
        if authz == nil {
            newAuthz := ev1.NewDepositAuthorization(
                ev1.DepositAuthorizationScopes{ev1.DepositScopeDeployment}, 
                spendable,
            )
            k.authzKeeper.SaveGrant(sctx, owner, granter, newAuthz, nil)
        }
    }
}

Step 2: Payment Processing and Grant Consumption

During payment withdrawal, the escrow keeper retrieves the authorization, validates the remaining spend limit, and atomically updates the grant. The TryAccept method handles partial consumption logic:

// x/escrow/keeper/keeper.go – PaymentWithdraw excerpt
authz, exp := k.authzKeeper.GetAuthorization(ctx, owner, granter, ev1.MsgTypeDeposit)
depositAuthz, ok := authz.(ev1.Authorization)
if !ok { 
    return fmt.Errorf("invalid authz type") 
}

resp, err := depositAuthz.TryAccept(ctx, msg, true) // Reduces SpendLimit
if err != nil { 
    return err 
}

k.authzKeeper.SaveGrant(ctx, owner, granter, resp.Updated, exp) // Persist remaining allowance

This ensures that delegated permissions are decrementally consumed as providers withdraw payments, preventing over-spending beyond the original grant.

Step 3: Account Closure and Permission Cleanup

When a deployment closes or an account terminates, the system must revoke lingering permissions to prevent stale access. The AccountClose function in x/escrow/keeper/keeper.go calls DeleteGrant to remove the authorization:

// x/escrow/keeper/keeper.go – AccountClose
if err := k.authzKeeper.DeleteGrant(ctx, owner, granter, ev1.MsgTypeDeposit); err != nil {
    return err
}

This cleanup guarantees that closed deployments cannot incur future charges against the owner's delegated balances.

Handling Authorization During Chain Upgrades

The upgrades/software/v1.0.0/upgrade.go file contains migration logic that iterates over existing authz grants, replacing outdated DepositAuthorization schemas with updated versions. This ensures that delegated permissions remain valid across protocol upgrades without requiring users to re-authorize manually.

The upgrade handler uses IterateGrants to locate all escrow-related authorizations and SaveGrant to persist the migrated structures with updated type URLs and scope definitions.

Summary

  • AuthzKeeper Interface: A thin wrapper in x/escrow/keeper/external.go abstracts the Cosmos SDK x/authz module, enabling clean dependency injection and testing.
  • DepositAuthorization: A custom grant type in pkg/akt.dev/go/node/escrow/v1 that carries spend limits and scopes, implementing TryAccept for partial consumption.
  • Automatic Grant Lifecycle: Deployments trigger AuthorizeDeposits to validate or create grants, payments consume them via GetAuthorization and SaveGrant, and closures invoke DeleteGrant for cleanup.
  • Upgrade Compatibility: The v1.0.0 upgrade handler migrates legacy authorization schemas to maintain grant validity across chain upgrades.

Frequently Asked Questions

How does the Deployment module verify deposits without direct authz dependencies?

The Deployment module delegates all deposit validation to the Escrow module's AuthorizeDeposits function. This function acts as the single integration point with the AuthzKeeper interface, checking whether the deposit source is the owner's direct balance (SourceBalance) or an existing authorization grant (SourceAuthz).

What happens to unused authorization grants when a deployment closes?

The Escrow keeper's AccountClose function automatically invokes DeleteGrant on the AuthzKeeper interface, immediately revoking the DepositAuthorization associated with the deployment. This prevents any future withdrawals against the owner's account using that specific grant.

Can authorization grants be partially consumed across multiple payments?

Yes. The DepositAuthorization type implements the TryAccept method, which validates the requested amount against the remaining SpendLimit and returns an updated authorization with the reduced balance. The escrow keeper saves this updated grant via SaveGrant, allowing the remaining allowance to be used for future payments until fully exhausted.

Where is the authorization logic configured during application startup?

The concrete Cosmos SDK authz keeper is injected into the Escrow module via the AuthzKeeper interface during app initialization in app/types/app.go. This wiring occurs around lines 281-283, where the application sets app.Keepers.Cosmos.Authz and passes it to the escrow keeper constructor.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →