> ## Documentation Index
> Fetch the complete documentation index at: https://docs.magicblock.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# アクセス制御

> Private Ephemeral Rollups におけるきめ細かなアクセス制御とメンバー権限の管理方法を学びます。

***

<CardGroup cols={2}>
  <Card title="Permission Program" icon="github" iconType="duotone" disabled>
    オンチェーン権限管理（近日公開）
  </Card>

  <Card title="Ephemeral Rollups SDK" icon="github" href="https://github.com/magicblock-labs/ephemeral-rollups-sdk" iconType="duotone">
    Private Ephemeral Rollups 向け SDK
  </Card>
</CardGroup>

***

## 概要

Private Ephemeral Rollups は、[コンプライアンス](/jp/pages/private-ephemeral-rollups-pers/introduction/compliance-framework)を中核に据えた [Trusted Execution Environment](/jp/pages/private-ephemeral-rollups-pers/introduction/onchain-privacy) 上で、permissioned accounts に対するきめ細かな権限制御を可能にする [Ephemeral Rollups](/jp/pages/ephemeral-rollups-ers/introduction/why) です。各 permission account は、メンバー一覧と、それぞれが実行できる操作を決める特定のフラグを保持します。

### 基本概念

* **Permission Account**: 特定のアカウントに対するアクセス制御ルールを保存する PDA
* **Members**: フラグを通じて特定の権限を付与されたアドレス
* **Flags**: メンバーが何をできるか（authority、ログ閲覧、残高閲覧など）を定義するビットマスク
* **Public Permissions**: メンバーが `None` に設定されると、permissioned account は一時的に可視状態になる

***

## メンバーフラグ

メンバーフラグは、各メンバーに対するきめ細かな権限を定義します。フラグはビット OR で組み合わせることで、複数の権限を付与できます。

**フラグの説明:**

* **AUTHORITY**: 権限設定の更新・委任、他メンバーの追加・削除、メンバーフラグの更新を許可します。
* **TX\_LOGS**: トランザクション実行ログの閲覧を許可します。
* **TX\_BALANCES**: アカウント残高の変化の閲覧を許可します。
* **TX\_MESSAGE**: トランザクションメッセージデータの閲覧を許可します。
* **ACCOUNT\_SIGNATURES**: アカウント署名の閲覧を許可します

<Tabs>
  <Tab title="Rust SDK">
    ```rust theme={null}
    use ephemeral_rollups_sdk::access_control::structs::{
        Member,
        AUTHORITY_FLAG,
        TX_LOGS_FLAG,
        TX_BALANCES_FLAG,
        TX_MESSAGE_FLAG,
        ACCOUNT_SIGNATURES_FLAG,
    };

    // Set flags by combining them with bitwise OR
    let flags = AUTHORITY_FLAG | TX_LOGS_FLAG;

    // Create a member with combined flags
    let mut member = Member {
        flags,
        pubkey: user_pubkey,
    };

    // Check if member has a specific flag using bitwise AND
    let is_authority = (member.flags & AUTHORITY_FLAG) != 0;
    let can_see_logs = (member.flags & TX_LOGS_FLAG) != 0;

    // Use helper methods to set/remove flags
    member.set_flags(TX_BALANCES_FLAG); // Add a flag
    member.remove_flags(TX_LOGS_FLAG);  // Remove a flag
    ```
  </Tab>

  <Tab title="Pinocchio">
    ```rust theme={null}
    use ephemeral_rollups_pinocchio::types::{Member, MemberFlags};
    use pinocchio::Address;

    // Create and set flags using individual methods
    let mut flags = MemberFlags::new();
    flags.set(MemberFlags::AUTHORITY);
    flags.set(MemberFlags::TX_LOGS);
    flags.set(MemberFlags::TX_BALANCES);

    // Create a member with flags
    let member = Member {
        flags,
        pubkey: user_address,
    };

    // Remove a flag
    flags.remove(MemberFlags::TX_LOGS);

    // Create flags from individual boolean values
    let flags = MemberFlags::from_acl_flags(
        true,  // authority
        true,  // tx_logs
        false, // tx_balances
        true,  // tx_message
        false, // account_signatures
    );

    // Convert flags to byte value
    let flag_byte = flags.to_acl_flag_byte();

    // Create flags from byte value
    let flags = MemberFlags::from_acl_flag_byte(flag_byte);
    ```
  </Tab>

  <Tab title="Web3.js">
    ```typescript theme={null}
    import { PublicKey } from "@solana/web3.js";
    import {
      AUTHORITY_FLAG,
      TX_LOGS_FLAG,
      TX_BALANCES_FLAG,
      TX_MESSAGE_FLAG,
      ACCOUNT_SIGNATURES_FLAG,
      type Member,
    } from "@magicblock-labs/ephemeral-rollups-sdk";

    // Set flags by combining them with bitwise OR
    const flags = AUTHORITY_FLAG | TX_LOGS_FLAG;

    // Create a member with combined flags
    const member: Member = {
      flags,
      pubkey: new PublicKey(userAddress),
    };

    // Check if a flag is present using bitwise AND
    const isAuthority = (member.flags & AUTHORITY_FLAG) !== 0;
    const canSeeLogs = (member.flags & TX_LOGS_FLAG) !== 0;
    const canSeeBalances = (member.flags & TX_BALANCES_FLAG) !== 0;

    // Add a flag to existing flags
    const updatedFlags = member.flags | TX_BALANCES_FLAG;

    // Remove a flag from existing flags
    const removedFlags = member.flags & ~TX_LOGS_FLAG;
    ```
  </Tab>

  <Tab title="Kit">
    ```typescript theme={null}
    import {
      AUTHORITY_FLAG,
      TX_LOGS_FLAG,
      TX_BALANCES_FLAG,
      TX_MESSAGE_FLAG,
      ACCOUNT_SIGNATURES_FLAG,
      isAuthority,
      canSeeTxLogs,
      canSeeTxBalances,
      canSeeTxMessages,
      canSeeAccountSignatures,
      type Member,
    } from "@magicblock-labs/ephemeral-rollups-sdk";

    // Set flags by combining them with bitwise OR
    const flags = AUTHORITY_FLAG | TX_LOGS_FLAG | TX_BALANCES_FLAG;

    // Create a member with combined flags
    const member: Member = {
      flags,
      pubkey: userAddress,
    };

    // Use helper functions to check specific permissions
    const canModifyPermission = isAuthority(member, userAddress);
    const canViewLogs = canSeeTxLogs(member, userAddress);
    const canViewBalances = canSeeTxBalances(member, userAddress);
    const canViewMessages = canSeeTxMessages(member, userAddress);
    const canViewSignatures = canSeeAccountSignatures(member, userAddress);

    // Add a flag to existing member
    const updatedFlags = member.flags | TX_MESSAGE_FLAG;

    // Remove a flag from existing member
    const removedFlags = member.flags & ~TX_LOGS_FLAG;
    ```
  </Tab>
</Tabs>

***

## Ephemeral Permission

<Note>
  `EphemeralPermission` アカウントは Ephemeral Rollup 上で完結し、レントは委任済みの
  PDA が負担します — Base Layer に permission account を作成・委任・commit-and-undelegate
  する必要はありません。ライフサイクル全体は **Create**、**Update**、**Close** の
  3 つの CPI 操作でカバーされ、すべて ER 上のデータアカウントが PDA として署名し、
  MagicBlock の Permission Program `ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1` 経由で実行されます。
</Note>

**前提条件 — データ PDA を委任する。** 委任されるのはデータアカウントのみです
（Base Layer 上で、MagicBlock の Delegation Program
`DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh` 経由）。委任後、PDA は自身のプログラム
シードを使って ER 上で 3 つの permission 操作すべてに署名し、ephemeral permission の
レントを支払います — そのため `initialize` 時に事前にチャージしておく必要があります。
エンドツーエンドの流れは
[クイックスタート](/jp/pages/private-ephemeral-rollups-pers/how-to-guide/quickstart#2-委任と-permission-の作成)
を参照してください。

<Note>
  <p>
    これらの公開バリデータは開発用として利用できます。委任命令には、
    対象となる ER バリデータを必ず追加してください。
  </p>

  **メインネット**

  <ul>
    <li>
      アジア (as.magicblock.app):{" "}
      <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
    </li>

    <li>
      EU (eu.magicblock.app):{" "}
      <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
    </li>

    <li>
      米国 (us.magicblock.app):{" "}
      <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
    </li>

    <li>
      TEE (mainnet-tee.magicblock.app):{" "}
      <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
    </li>
  </ul>

  **Devnet**

  <ul>
    <li>
      アジア (devnet-as.magicblock.app):{" "}
      <code>MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57</code>
    </li>

    <li>
      EU (devnet-eu.magicblock.app):{" "}
      <code>MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e</code>
    </li>

    <li>
      米国 (devnet-us.magicblock.app):{" "}
      <code>MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd</code>
    </li>

    <li>
      TEE (devnet-tee.magicblock.app):{" "}
      <code>MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo</code>
    </li>
  </ul>

  **ローカルネット**

  <ul>
    <li>
      ローカル ER (localhost:7799):{" "}
      <code>mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev</code>
    </li>
  </ul>
</Note>

<Steps>
  <Step title={<a href="#1-ephemeral-permission-を作成">Ephemeral Permission を作成</a>}>
    初期メンバーとプライバシーフラグを指定して、ER 上に新しい `EphemeralPermission`
    アカウントを初期化します。べき等 — 既に存在する場合はスキップします。
  </Step>

  <Step title={<a href="#2-ephemeral-permission-を更新">Ephemeral Permission を更新</a>}>
    プライバシーフラグの切り替え、またはメンバーの追加 / 削除 / フラグ変更を行います。
    更新は ER 上で即座に反映されます。
  </Step>

  <Step title={<a href="#3-ephemeral-permission-を閉じる">Ephemeral Permission を閉じる</a>}>
    permission が不要になった時点で、ER 上の `EphemeralPermission` アカウントを閉じ、
    レントをデータ PDA に返金します。
  </Step>
</Steps>

***

## Ephemeral Permission 操作

<Tabs>
  <Tab title="1. Ephemeral Permission を作成">
    `CreateEphemeralPermissionCpi` を使って ER 上に新しい `EphemeralPermission`
    アカウントを初期化します。Payer は委任済みのデータ PDA で、自身のプログラムシードで
    署名し、`initialize` 時に事前チャージされた lamports からレントを支払います。

    <Tabs>
      <Tab title="Anchor">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::{
            instructions::CreateEphemeralPermissionCpi,
            structs::{EphemeralMembersArgs, Member},
        };

        // Counter PDA pays for its own permission rent (it carries lamports onto the ER
        // after delegation and signs as PDA via seeds).
        let signers = [
            COUNTER_SEED,
            ctx.accounts.counter.authority.as_ref(),
            &[ctx.bumps.counter],
        ];

        CreateEphemeralPermissionCpi {
            payer: ctx.accounts.counter.to_account_info(),                  // pays ephemeral rent
            permissioned_account: ctx.accounts.counter.to_account_info(),   // what the permission gates
            permission: ctx.accounts.permission.to_account_info(),
            vault: ctx.accounts.ephemeral_vault.to_account_info(),
            magic_program: ctx.accounts.magic_program.to_account_info(),
            permission_program: ctx.accounts.permission_program.to_account_info(),
            args: EphemeralMembersArgs {
                is_private: false,    // start public — flip via UpdateEphemeralPermission
                members: vec![],
            },
        }
        .invoke_signed(&[&signers])?;
        ```
      </Tab>

      <Tab title="Rust SDK">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::{
            instructions::CreateEphemeralPermissionCpi,
            structs::{EphemeralMembersArgs, Member},
        };

        // `permissioned_account` (here a counter PDA) signs as PDA via seeds; pass the
        // same seeds you used for `find_program_address` to derive it.
        let seeds: &[&[u8]] = &[
            COUNTER_SEED,
            permissioned_account_authority.as_ref(),
            &[bump],
        ];

        CreateEphemeralPermissionCpi {
            payer: &counter_account_info,                // pays ephemeral rent
            permissioned_account: &counter_account_info, // what the permission gates
            permission: &permission_account_info,
            vault: &ephemeral_vault_account_info,
            magic_program: &magic_program_account_info,
            permission_program: &permission_program_account_info,
            args: EphemeralMembersArgs {
                is_private: false,   // start public — flip via UpdateEphemeralPermission
                members: vec![],
            },
        }
        .invoke_signed(&[seeds])?;
        ```
      </Tab>

      <Tab title="Pinocchio">
        ```rust theme={null}
        use ephemeral_rollups_pinocchio::acl::{
            CreateEphemeralPermission, EphemeralMembersArgs, Member,
        };
        use pinocchio::cpi::{Seed, Signer};

        // Buffer size: discriminator (8) + EphemeralMembersArgs body.
        // 64 bytes covers up to 1 member with slack for future Update calls.
        const PERMISSION_CPI_BUF: usize = 64;

        // PDA-signed CPI — the counter PDA pays rent and authorizes the permission.
        let bump_seed = [bump];
        let seeds_array: [Seed; 3] = [
            Seed::from(b"counter"),
            Seed::from(authority.address().as_ref()),
            Seed::from(&bump_seed),
        ];
        let signer = Signer::from(&seeds_array);

        let members: [Member; 0] = [];   // start public; toggle via Update
        CreateEphemeralPermission {
            payer: counter_account,
            permissioned_account: counter_account,
            permission,
            vault,
            magic_program,
            permission_program,
            args: EphemeralMembersArgs {
                is_private: false,
                members: &members,
            },
        }
        .invoke_signed::<PERMISSION_CPI_BUF>(&[signer])?;
        ```
      </Tab>

      <Tab title="Kit">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { pipe, createTransactionMessage, appendTransactionMessageInstructions } from "@solana/kit";

        // EphemeralPermissions are created on the ER by the delegated PDA (via the
        // user-program's wrapper instruction). Submit to the ER connection, not base.
        const initIx = await counterProgram.methods
          .initPermission()
          .accountsPartial({
            authority: tempKeypair.address,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const transactionMessage = pipe(
          createTransactionMessage({ version: 0 }),
          (tx) => appendTransactionMessageInstructions([initIx], tx),
        );
        const sig = await ephemeralConnection.sendAndConfirmTransaction(
          transactionMessage,
          [tempKeypair],
          { commitment: "confirmed" },
        );
        console.log("init_permission tx:", sig);
        ```
      </Tab>

      <Tab title="Web3.js">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

        // EphemeralPermissions are created on the ER by the delegated PDA (via the
        // user-program's wrapper instruction). Submit to the ER connection, not base.
        const initIx = await counterProgram.methods
          .initPermission()
          .accountsPartial({
            authority: tempKeypair.publicKey,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const tx = new Transaction().add(initIx);
        const sig = await sendAndConfirmTransaction(ephemeralConnection, tx, [tempKeypair]);
        console.log("init_permission tx:", sig);
        ```
      </Tab>
    </Tabs>

    **ユースケース:**

    * ER 上で新たに委任された PDA のアクセス制御をブートストラップ
    * 公開状態（`is_private: false`、メンバー空）で開始し、後で Update により絞り込む

    [⬆️ トップに戻る](#ephemeral-permission)
  </Tab>

  <Tab title="2. Ephemeral Permission を更新">
    `UpdateEphemeralPermissionCpi` でプライバシーフラグを切り替え、メンバー一覧を
    書き換えます。毎回 authority を含む完全なメンバー一覧を再構築してください —
    そうすればデータ PDA が自分自身をロックアウトすることはありません。

    <Tabs>
      <Tab title="Anchor">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::{
            instructions::UpdateEphemeralPermissionCpi,
            structs::{
                EphemeralMembersArgs, Member,
                TX_LOGS_FLAG, TX_MESSAGE_FLAG, TX_BALANCES_FLAG,
            },
        };

        let signers = [
            COUNTER_SEED,
            ctx.accounts.counter.authority.as_ref(),
            &[ctx.bumps.counter],
        ];

        // When private, only listed members can read ER state via the TEE.
        // Empty member list + is_private=false = fully public.
        let members = if is_private {
            vec![Member {
                flags: TX_LOGS_FLAG | TX_MESSAGE_FLAG | TX_BALANCES_FLAG,
                pubkey: ctx.accounts.counter.authority,
            }]
        } else {
            vec![]
        };

        UpdateEphemeralPermissionCpi {
            payer: ctx.accounts.counter.to_account_info(),
            permissioned_account: ctx.accounts.counter.to_account_info(),
            permission: ctx.accounts.permission.to_account_info(),
            vault: ctx.accounts.ephemeral_vault.to_account_info(),
            magic_program: ctx.accounts.magic_program.to_account_info(),
            permission_program: ctx.accounts.permission_program.to_account_info(),
            authority: ctx.accounts.counter.to_account_info(),
            authority_is_signer: false,    // PDA signs via the seeds above
            args: EphemeralMembersArgs { is_private, members },
        }
        .invoke_signed(&[&signers])?;
        ```
      </Tab>

      <Tab title="Rust SDK">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::{
            instructions::UpdateEphemeralPermissionCpi,
            structs::{
                EphemeralMembersArgs, Member,
                TX_LOGS_FLAG, TX_MESSAGE_FLAG, TX_BALANCES_FLAG,
            },
        };

        let seeds: &[&[u8]] = &[
            COUNTER_SEED,
            permissioned_account_authority.as_ref(),
            &[bump],
        ];

        // When private, only listed members can read ER state via the TEE.
        let members = if is_private {
            vec![Member {
                flags: TX_LOGS_FLAG | TX_MESSAGE_FLAG | TX_BALANCES_FLAG,
                pubkey: permissioned_account_authority,
            }]
        } else {
            vec![]
        };

        UpdateEphemeralPermissionCpi {
            payer: &counter_account_info,
            permissioned_account: &counter_account_info,
            permission: &permission_account_info,
            vault: &ephemeral_vault_account_info,
            magic_program: &magic_program_account_info,
            permission_program: &permission_program_account_info,
            authority: &counter_account_info,
            authority_is_signer: false,   // PDA signs via the seeds above
            args: EphemeralMembersArgs { is_private, members },
        }
        .invoke_signed(&[seeds])?;
        ```
      </Tab>

      <Tab title="Pinocchio">
        ```rust theme={null}
        use ephemeral_rollups_pinocchio::acl::{
            EphemeralMembersArgs, Member, MemberFlags, UpdateEphemeralPermission,
        };
        use pinocchio::cpi::{Seed, Signer};

        const PERMISSION_CPI_BUF: usize = 64;

        let bump_seed = [bump];
        let seeds_array: [Seed; 3] = [
            Seed::from(b"counter"),
            Seed::from(authority.address().as_ref()),
            Seed::from(&bump_seed),
        ];
        let signer = Signer::from(&seeds_array);

        // Read the on-chain Counter to grab `authority` — the sole "private" member.
        let counter_authority = {
            let data = counter_account.try_borrow()?;
            Counter::load(&data)?.authority
        };

        let single_member = [Member {
            flags: MemberFlags::from_acl_flag_byte(
                MemberFlags::TX_LOGS | MemberFlags::TX_MESSAGE | MemberFlags::TX_BALANCES,
            ),
            pubkey: counter_authority,
        }];
        let members: &[Member] = if is_private { &single_member } else { &[] };

        UpdateEphemeralPermission {
            payer: counter_account,
            permissioned_account: counter_account,
            permission,
            vault,
            magic_program,
            permission_program,
            authority: counter_account,
            authority_is_signer: false,   // PDA signs via the seeds above
            args: EphemeralMembersArgs { is_private, members },
        }
        .invoke_signed::<PERMISSION_CPI_BUF>(&[signer])?;
        ```
      </Tab>

      <Tab title="Kit">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { pipe, createTransactionMessage, appendTransactionMessageInstructions } from "@solana/kit";

        // Toggle the `is_private` flag. Idempotent — the program rebuilds the member
        // list every call so the authority never locks itself out.
        const updateIx = await counterProgram.methods
          .setPrivacy(isPrivate)
          .accountsPartial({
            authority: tempKeypair.address,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const transactionMessage = pipe(
          createTransactionMessage({ version: 0 }),
          (tx) => appendTransactionMessageInstructions([updateIx], tx),
        );
        const sig = await ephemeralConnection.sendAndConfirmTransaction(
          transactionMessage,
          [tempKeypair],
          { commitment: "confirmed" },
        );
        console.log("set_privacy tx:", sig);
        ```
      </Tab>

      <Tab title="Web3.js">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

        // Toggle the `is_private` flag. Idempotent — the program rebuilds the member
        // list every call so the authority never locks itself out.
        const updateIx = await counterProgram.methods
          .setPrivacy(isPrivate)
          .accountsPartial({
            authority: tempKeypair.publicKey,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const tx = new Transaction().add(updateIx);
        const sig = await sendAndConfirmTransaction(ephemeralConnection, tx, [tempKeypair]);
        console.log("set_privacy tx:", sig);
        ```
      </Tab>
    </Tabs>

    **ユースケース:**

    * 必要に応じて `is_private` を切り替える（例：プライベートプレイ、公開リビール）
    * `TX_LOGS | TX_MESSAGE | TX_BALANCES` フラグ付きで新しい閲覧者を追加
    * 次回の呼び出しのメンバー一覧から除外することでメンバーの権限を取り消す

    [⬆️ トップに戻る](#ephemeral-permission)
  </Tab>

  <Tab title="3. Ephemeral Permission を閉じる">
    `CloseEphemeralPermissionCpi` を使って ER 上の `EphemeralPermission` アカウントを
    閉じます。レントはデータ PDA（元の payer）に返金されます。オプション — permission
    が不要になった場合にのみ呼び出します。

    <Tabs>
      <Tab title="Anchor">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::instructions::CloseEphemeralPermissionCpi;

        let signers = [
            COUNTER_SEED,
            ctx.accounts.counter.authority.as_ref(),
            &[ctx.bumps.counter],
        ];

        // Refunds the permission's rent to `payer` (the counter PDA).
        CloseEphemeralPermissionCpi {
            payer: ctx.accounts.counter.to_account_info(),
            permissioned_account: ctx.accounts.counter.to_account_info(),
            permission: ctx.accounts.permission.to_account_info(),
            vault: ctx.accounts.ephemeral_vault.to_account_info(),
            magic_program: ctx.accounts.magic_program.to_account_info(),
            permission_program: ctx.accounts.permission_program.to_account_info(),
            authority: ctx.accounts.counter.to_account_info(),
            authority_is_signer: false,
        }
        .invoke_signed(&[&signers])?;
        ```
      </Tab>

      <Tab title="Rust SDK">
        ```rust theme={null}
        use ephemeral_rollups_sdk::access_control::instructions::CloseEphemeralPermissionCpi;

        let seeds: &[&[u8]] = &[
            COUNTER_SEED,
            permissioned_account_authority.as_ref(),
            &[bump],
        ];

        // Refunds the permission's rent to `payer` (the counter PDA).
        CloseEphemeralPermissionCpi {
            payer: &counter_account_info,
            permissioned_account: &counter_account_info,
            permission: &permission_account_info,
            vault: &ephemeral_vault_account_info,
            magic_program: &magic_program_account_info,
            permission_program: &permission_program_account_info,
            authority: &counter_account_info,
            authority_is_signer: false,
        }
        .invoke_signed(&[seeds])?;
        ```
      </Tab>

      <Tab title="Pinocchio">
        ```rust theme={null}
        use ephemeral_rollups_pinocchio::acl::CloseEphemeralPermission;
        use pinocchio::cpi::{Seed, Signer};

        let bump_seed = [bump];
        let seeds_array: [Seed; 3] = [
            Seed::from(b"counter"),
            Seed::from(authority.address().as_ref()),
            Seed::from(&bump_seed),
        ];
        let signer = Signer::from(&seeds_array);

        // Refunds the permission's rent to `payer` (the counter PDA).
        CloseEphemeralPermission {
            payer: counter_account,
            permissioned_account: counter_account,
            permission,
            vault,
            magic_program,
            permission_program,
            authority: counter_account,
            authority_is_signer: false,
        }
        .invoke_signed(&[signer])?;
        ```
      </Tab>

      <Tab title="Kit">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { pipe, createTransactionMessage, appendTransactionMessageInstructions } from "@solana/kit";

        // Refunds the permission's rent to the counter PDA.
        const closeIx = await counterProgram.methods
          .closePermission()
          .accountsPartial({
            authority: tempKeypair.address,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const transactionMessage = pipe(
          createTransactionMessage({ version: 0 }),
          (tx) => appendTransactionMessageInstructions([closeIx], tx),
        );
        const sig = await ephemeralConnection.sendAndConfirmTransaction(
          transactionMessage,
          [tempKeypair],
          { commitment: "confirmed" },
        );
        console.log("close_permission tx:", sig);
        ```
      </Tab>

      <Tab title="Web3.js">
        ```typescript theme={null}
        import {
          MAGIC_PROGRAM_ID,
          PERMISSION_PROGRAM_ID,
          EPHEMERAL_VAULT_ID,
        } from "@magicblock-labs/ephemeral-rollups-sdk";
        import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

        // Refunds the permission's rent to the counter PDA.
        const closeIx = await counterProgram.methods
          .closePermission()
          .accountsPartial({
            authority: tempKeypair.publicKey,
            counter: counterPda,
            permission: permissionPda,
            permissionProgram: PERMISSION_PROGRAM_ID,
            ephemeralVault: EPHEMERAL_VAULT_ID,
            magicProgram: MAGIC_PROGRAM_ID,
          })
          .instruction();

        const tx = new Transaction().add(closeIx);
        const sig = await sendAndConfirmTransaction(ephemeralConnection, tx, [tempKeypair]);
        console.log("close_permission tx:", sig);
        ```
      </Tab>
    </Tabs>

    **ユースケース:**

    * データ PDA を委任解除する前に ephemeral レントを回収
    * プライベート実行が終わったらアクセス制御を取り壊す

    [⬆️ トップに戻る](#ephemeral-permission)
  </Tab>
</Tabs>

<Tip>
  参考実装:
  [`private-counter`](https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/private-counter)
  （Anchor）と
  [`pinocchio-private-counter`](https://github.com/magicblock-labs/magicblock-engine-examples/tree/main/pinocchio-private-counter)。
</Tip>

***

## ベストプラクティス

1. **Authority 管理**: 常に少なくとも 1 人の信頼できるメンバーに `AUTHORITY_FLAG` を割り当てる
2. **最小権限**: 各メンバーには必要なフラグだけを付与する
3. **リアルタイム更新**: permission は委任解除せずに Private Ephemeral Rollup 上でリアルタイム更新でき、動的なアクセス制御調整が可能
4. **クリーンアップ**: 未使用の permission accounts を委任解除して閉じ、SOL を解放する

***

## セキュリティ上の考慮事項

* **署名者の検証**: `AUTHORITY_FLAG` を持つメンバー、または permissioned account を持つプログラムだけが変更を認可できます
* **公開アカウント**: メンバーを `None` に設定すると、アカウントは公開可視状態になります
* **デフォルト Authority**: デフォルトでは、permissioned account の所有者が permission account の members に permission authority として追加されます
* **空のメンバー一覧**: `members` フィールドが空リストに設定されると、permissioned account は完全に制限されて非公開になります。permission を変更できるのは所有者だけです
* **アクセス監査**: メンバーフラグを使ってアクセスを監査・制御します

***

<CardGroup cols={2}>
  <Card title="アクセス制御" icon="lock" href="/jp/pages/private-ephemeral-rollups-pers/how-to-guide/access-control" iconType="duotone">
    きめ細かなアクセス制御
  </Card>

  <Card title="オンチェーンプライバシー" icon="shield" href="/jp/pages/private-ephemeral-rollups-pers/introduction/onchain-privacy" iconType="duotone">
    プライバシーの仕組みと概念
  </Card>

  <Card title="認可" icon="key" href="/jp/pages/private-ephemeral-rollups-pers/introduction/authorization" iconType="duotone">
    認可フレームワーク
  </Card>

  <Card title="コンプライアンスフレームワーク" icon="certificate" href="/jp/pages/private-ephemeral-rollups-pers/introduction/compliance-framework" iconType="duotone">
    コンプライアンス基準とガイドライン
  </Card>
</CardGroup>
