# Sandboxed NFT Gallery Architecture in Nautilus Wallet: A Technical Deep Dive

> Explore the sandboxed NFT gallery architecture in Nautilus Wallet. Discover how Vue 3 components and isolated iframes secure untrusted NFT media for IPFS and HTTP content.

- Repository: [Nautilus Team/nautilus-wallet](https://github.com/nautls/nautilus-wallet)
- Tags: architecture
- Published: 2026-03-07

---

**The Nautilus Wallet implements a sandboxed NFT gallery using Vue 3 components that render untrusted NFT media inside restrictive iframes pointing to an isolated Azure-hosted sandbox service, preventing malicious code execution while supporting IPFS and HTTP content.**

The Nautilus Wallet employs a sophisticated sandboxed NFT gallery architecture to safely display blockchain-based artwork without exposing users to potentially malicious embedded code. This Vue 3-based implementation isolates untrusted media through restrictive iframe sandboxes and remote rendering services. Understanding this architecture reveals how modern cryptocurrency wallets balance rich media experiences with stringent security requirements.

## Core Architecture of the Sandboxed NFT Gallery

### Component Hierarchy and Responsibilities

The gallery follows a layered component architecture where each layer handles specific concerns:

- **AssetsView.vue** ([`src/views/assets/AssetsView.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/views/assets/AssetsView.vue)) – Renders the responsive grid layout and iterates over collectible assets
- **AssetInfoDialog.vue** ([`src/components/asset/AssetInfoDialog.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/components/asset/AssetInfoDialog.vue)) – Displays detailed NFT information using the same sandbox component for larger previews
- **AssetImageSandbox.vue** ([`src/components/asset/AssetImageSandbox.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/components/asset/AssetImageSandbox.vue)) – Core wrapper that constructs sandboxed iframes and handles IPFS resolution

### Type System and Asset Classification

The wallet uses strict typing to identify NFT assets. In [`src/types/internal.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/internal.ts), the system defines:

```typescript
enum AssetType {
  NFT = "NFT",
  // ... other types
}

enum AssetSubtype {
  PictureArtwork = "PictureArtwork",
  AudioArtwork = "AudioArtwork", 
  VideoArtwork = "VideoArtwork"
}

```

The GraphQL service in [`src/chains/ergo/services/graphQlService.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/services/graphQlService.ts) detects NFTs by checking if `assetInfo.type === AssetType.NFT` or if the encoded string starts with the NFT prefix.

## Data Flow and Rendering Pipeline

### IPFS Resolution and Gateway Configuration

Before sandboxing, the system resolves IPFS URIs to user-configured gateways. The `AssetImageSandbox` component computes:

```typescript
const gateway = computed(() =>
  app.settings.ipfsGateway.endsWith("/") 
    ? app.settings.ipfsGateway 
    : `${app.settings.ipfsGateway}/`
);

function resolveIpfs(url?: string): string {
  if (!url) return "";
  if (!url.startsWith(IPFS_PROTOCOL_PREFIX)) return url;
  return url.replace(IPFS_PROTOCOL_PREFIX, gateway.value);
}

```

This ensures users control their IPFS infrastructure while preventing direct IPFS fetching by the wallet.

### Sandbox URL Construction and Iframe Isolation

The component constructs a query string for the remote sandbox service at `https://nautilus-nft-sandbox.azurewebsites.net`:

```typescript
const sandboxUrl = computed(() => {
  if (!props.src) return;
  let query = "";
  if (props.height) query += `&height=${props.height}`;
  if (props.objectFit) query += `&fit=${props.objectFit}`;
  if (props.overflow) query += `&overflow=${props.overflow}`;
  return `${CONTENT_SANDBOX_URL}/?url=${encodeURIComponent(content.value)}${query}`;
});

```

The iframe uses the most restrictive sandbox settings:

```html
<iframe
  v-show="!loading && sandboxUrl"
  :key="sandboxUrl"
  :src="sandboxUrl"
  sandbox=""
  class="m-0 p-0"
  frameborder="0"
  @load="loading = false"
/>

```

The empty `sandbox` attribute disables scripts, same-origin access, forms, and other privileged operations.

## Security Guarantees of the Sandboxed Architecture

The Nautilus Wallet implements defense-in-depth for NFT rendering:

- **Script execution prevention** – The empty `sandbox` attribute on iframes prevents JavaScript execution, mitigating XSS attacks from malicious NFT metadata.
- **Origin isolation** – Content renders in a separate Azure-hosted sandbox service (`nautilus-nft-sandbox.azurewebsites.net`), isolating the wallet's extension context from the media domain.
- **User-controlled IPFS gateways** – The wallet never fetches IPFS content directly; users specify their gateway in `app.settings.ipfsGateway`, limiting exposure to trusted infrastructure.
- **MIME type validation** – The sandbox service validates content types before rendering, preventing polyglot attacks where non-media files masquerade as images.

## Implementation Examples

### Rendering NFT Tiles in the Gallery Grid

To display NFT artwork in a responsive grid, use the `AssetImageSandbox` component with sizing constraints:

```vue
<template>
  <div class="grid grid-cols-3 gap-4">
    <AssetImageSandbox
      v-for="nft in collectibles"
      :key="nft.tokenId"
      :src="nft.metadata?.artworkUrl"
      class="h-40 w-full rounded-md"
      height="10rem"
      object-fit="cover"
      overflow="hidden"
      display-external-link
    />
  </div>
</template>

<script setup lang="ts">
import { AssetImageSandbox } from "@/components/asset";
import { computed } from "vue";
import { AssetType } from "@/types/internal";

const props = defineProps<{ assets: Asset[] }>();

const collectibles = computed(() => 
  props.assets.filter(a => a.type === AssetType.NFT)
);
</script>

```

### Opening Detailed NFT Previews

For full-size previews in a modal dialog, the `AssetInfoDialog` component reuses the same sandbox infrastructure:

```vue
<template>
  <AssetInfoDialog ref="dialogRef">
    <AssetImageSandbox
      display-external-link
      :src="contentUrl"
      object-fit="cover"
      class="border-input min-h-[240px] w-full rounded-md border"
    />
  </AssetInfoDialog>
</template>

<script setup lang="ts">
import { ref } from "vue";
import { AssetInfoDialog, AssetImageSandbox } from "@/components/asset";

const dialogRef = ref<InstanceType<typeof AssetInfoDialog>>();
const contentUrl = ref("ipfs://Qm.../artwork.png");

function openAssetInfoDialog(tokenId: string) {
  dialogRef.value?.open();
}
</script>

```

## Summary

- The **sandboxed NFT gallery** in Nautilus Wallet isolates untrusted media using restrictive iframes pointing to a remote Azure sandbox service.
- **AssetImageSandbox.vue** handles IPFS resolution, URL construction, and iframe rendering with empty `sandbox` attributes that disable scripts and same-origin access.
- The architecture supports **extensible media types** (Picture, Audio, Video) through the `AssetSubtype` enum and user-configurable IPFS gateways.
- Security relies on **defense-in-depth**: origin isolation, script sandboxing, user-controlled gateways, and MIME validation at the sandbox service level.

## Frequently Asked Questions

### How does the Nautilus Wallet prevent malicious code in NFTs from affecting the wallet?

The wallet renders all NFT media inside an iframe with an empty `sandbox` attribute in [`AssetImageSandbox.vue`](https://github.com/nautls/nautilus-wallet/blob/main/AssetImageSandbox.vue). This disables JavaScript execution, same-origin access, form submission, and other privileged operations. Additionally, the content loads from a separate Azure-hosted sandbox service (`nautilus-nft-sandbox.azurewebsites.net`), providing origin isolation from the wallet's extension context.

### Can users choose their own IPFS gateway for loading NFT artwork?

Yes. The `AssetImageSandbox` component reads the `ipfsGateway` setting from `app.settings` and resolves `ipfs://` URIs to the user-specified gateway before constructing the sandbox URL. This allows users to trust their own infrastructure or preferred public gateways rather than relying on hardcoded endpoints, limiting exposure to potentially malicious IPFS nodes.

### What types of NFT media does the sandboxed gallery support?

The type system in [`src/types/internal.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/internal.ts) defines `AssetSubtype.PictureArtwork`, `AssetSubtype.AudioArtwork`, and `AssetSubtype.VideoArtwork`. While the current implementation primarily renders visual artwork through the sandboxed iframe, the architecture supports extending the sandbox service to handle audio and video MIME types without requiring changes to the Vue component layer, as the same URL construction and isolation mechanisms apply.