# Build and submit an Environment

Create a small React Three Fiber world, build it into a bundle, open those
bytes in the Al-Amr desktop client, and submit an immutable revision for
review. This quickstart deliberately chooses the optional official R3F/WebGPU
authoring profile. It is one supported starting point, not the definition of
an Environment: the public Environment boundary is engine-neutral and can use
DOM, Canvas, WebGL, WebGPU, WASM, or any web engine.

## Result

At the end you will have:

- a built Environment bundle with `index.html` at its root;
- a local development copy opened by the Al-Amr desktop client;
- a Registry Project linked to a distinct Registry-assigned Environment ID;
- a checked revision whose uploaded bytes are bound to a SHA-512 integrity;
- a publication submission sent for human review.

Review is a controlled step. `alamr publish` submits the checked revision; it
does not approve or publish it immediately.

## Prerequisites

- Node.js 22 or newer and npm
- the Al-Amr desktop client installed on the development machine
- WebGPU or WebGL2 support for this quickstart's R3F profile (see the
  [rendering support policy](/docs/product/rendering-support))
- access to an Al-Amr Registry
- an Al-Amr developer account for `login`, `link`, and publication

Every copyable command below uses the complete pinned
`npx @al-amr/cli@0.1.0-alpha.7` invocation. These public npm bytes support
scaffolding, validation, account linking, bundle upload, and review submission.
After scaffold, use the package manager recorded in the generated
`package.json` for dependencies and project scripts. The official Context Pack
remains blocked until promotion.

The public CLI and generated project default to the production Registry at
`https://registry.al-amr.com`. Passing `--registry <url>` to `alamr create`, or
setting `AL_AMR_REGISTRY_URL` for that command, records the selected Registry
for local tooling. Keep that CLI setting for later commands when targeting a
non-default deployment. Repository-local platform development must select
`http://localhost:4000` explicitly. Never place a secret in a `VITE_*`
variable.

## 1. Scaffold the project

```sh
npx @al-amr/cli@0.1.0-alpha.7 create environment my-world
cd my-world
npm install
npx @al-amr/cli@0.1.0-alpha.7 validate --json
```

The Start page also provides the exact promoted `create-al-amr` bootstrap for
each supported package manager. The generated project pins real public package
versions and includes:

- `al-amr.environment.json` as the local Environment declaration;
- `AGENTS.md` with project rules for humans and coding agents;
- an SDK configuration derived from the manifest;
- `AlAmrProvider`, `AlAmrRuntimeGate`, and the R3F canvas;
- Vite build output rooted at `dist/index.html`;
- a bundle-relative `public/environment-cover.jpg` copied into `dist`;
- no `workspace:*` dependency.

A successful validation returns process code `0`, `ok: true`, and the message
`environment manifest is valid.`

The generated dependencies on `@al-amr/r3f` and `@react-three/fiber` are local
metadata that select `al-amr.r3f-webgpu@1`. They do not add an `engine` or
`renderer` field to `al-amr.environment.json`. A generic Environment may omit
those profile markers and choose another web stack. Generic
`runEnvironmentConformance` validates the common host contract; this starter's
`runR3fEnvironmentConformance` binding composes those checks with the optional
profile's gate and Canvas properties.

## 2. Describe the destination

The generated manifest is already a current bundle declaration. Edit its
human-facing fields and keep the bundle entry shape:

```json
{
  "contractVersion": "0.1.0-alpha.7",
  "environmentId": "env_local_example",
  "name": "My World",
  "shortDescription": "A quiet three-dimensional gathering place.",
  "category": "custom",
  "entry": { "kind": "bundle", "version": "0.1.0" },
  "portal": {
    "title": "My World",
    "blurb": "Enter a quiet gathering place.",
    "art": {
      "imageUrl": "environment-cover.jpg",
      "alt": "A quiet courtyard under a green evening sky"
    },
    "visibility": "private"
  },
  "capabilities": ["identity", "presence"],
  "plugins": []
}
```

The scaffold generates a unique local `environmentId`; do not copy the example
value above. `alamr link` replaces that local ID with the Registry-assigned
stable ID. The Project ID stored in `.al-amr/project.json` is a separate
management identity.

`entry.version` is the publisher's version for these bundle bytes. Bump it when
publishing changed immutable bytes. `entry.minClient` may optionally declare
the oldest compatible desktop client. A current bundle entry has no URL,
authorization callback, or logout callback: the desktop Shell is the only
OAuth client and serves verified bytes from
`alamr-env://<environmentId>/`. The cover path names a file inside the bundle,
copied from `public/environment-cover.jpg` to the build root.

### Optional: scaffold an Environment-owned backend

If the destination owns durable records or delegated domain roles, start with
the public backend template:

```sh
npx @al-amr/cli@0.1.0-alpha.7 create environment my-world --with-backend
```

The generated manifest declares the exact backend audience, endpoint, health
URL, scopes, context and data disclosures. The frontend requests a short-lived
Environment Backend Grant; `packages/backend` verifies it with
`@al-amr/backend` and live Registry introspection. Run both services with
`npm run dev`, then use `alamr doctor --json` to verify the manifest, Registry
and backend readiness.

Keep destination-specific data in this backend. Registry may provide the
initial `admin` boundary from Environment Project ownership, but roles such as
conference `organizer` and their audit history belong to the Environment
backend. Use HTTPS and durable storage in production; never accept a role or
raw account ID supplied by browser JSON.

## 3. Replace the starter object with your world

The generated `src/App.tsx` already contains the supported Runtime and
rendering boundaries:

```tsx
const runtimeClient = createEnvironmentRuntimeClient({ options: sdkConfig });

<EnvironmentRenderingGate
  mode={readRenderingModeOverride() ?? "auto"}
  environmentName={manifest.name}
>
  <AlAmrProvider client={runtimeClient}>
    <AlAmrRuntimeGate direction="ltr" environmentName={manifest.name}>
      <EnvironmentCanvas onFirstFrame={() => ready()}>
        <mesh castShadow position={[0, 0.7, 0]}>
          <boxGeometry args={[1.4, 1.4, 1.4]} />
          <meshStandardMaterial color="#20b875" />
        </mesh>
      </EnvironmentCanvas>
    </AlAmrRuntimeGate>
  </AlAmrProvider>
</EnvironmentRenderingGate>;
```

Replace the mesh with your scene. Keep Runtime-dependent content inside
`AlAmrRuntimeGate`, and keep durable session and controller state outside the
Canvas so renderer recovery cannot destroy it. The desktop Shell owns account
sign-in and injects the Environment bridge; the bundle never receives an OAuth,
access, ID, or refresh token. The Shell also owns the one platform Widget for
the active Environment; do not mount `AlAmrWidget` inside the bundle.

This starter's optional rendering profile comes from `@al-amr/r3f` (ADR-0048):
`EnvironmentRenderingGate` probes graphics capability before presence or
Plugins mount, and the shared `EnvironmentCanvas` creates the WebGPU-first
`WebGPURenderer` with an automatic WebGL2 fallback, one bounded retry,
device-loss recovery, and the bilingual unsupported page. Write custom
materials with TSL (`three/tsl`) only — GLSL `ShaderMaterial` does not run on
the WebGPU renderer. The full contract is in
[Rendering runtime](/docs/architecture/rendering-runtime).

Verify the application before contacting the Registry:

```sh
npm run typecheck
npm run build
npx @al-amr/cli@0.1.0-alpha.7 validate --json
```

## 4. Create and link the Registry Project

```sh
npx @al-amr/cli@0.1.0-alpha.7 login
npx @al-amr/cli@0.1.0-alpha.7 link
npx @al-amr/cli@0.1.0-alpha.7 validate --json
```

`login` authenticates the authoring CLI; it does not make the Environment an
OAuth client. `link` creates the matching Environment Project through the
public Management API when no Project ID is supplied. To link an existing
owned project, pass its `proj_*` ID.

After linking, inspect `al-amr.environment.json` and
`.al-amr/project.json`. The first contains the new `env_*` identity; the second
contains the separate `proj_*` identity and Registry origin.

## 5. Build and open it in the desktop client

```sh
npm run build
npx @al-amr/cli@0.1.0-alpha.7 dev --json
```

`alamr dev` requires a complete built folder with `index.html` at its root. It
copies `dist` into the desktop client's loose development-product directory and
returns an `alamr://environments/<environmentId>` link plus an open action. Open
that link in the installed client. Rebuild and rerun `alamr dev` after changes;
these development bytes stay outside the immutable publication ledger. Pass
`--bundle <dir>` when the build output is not `dist`.

## 6. Upload and check the immutable bundle

```sh
npm run build
npx @al-amr/cli@0.1.0-alpha.7 test --bundle dist --json --visibility unlisted --channel preview
```

For a bundle Environment, `alamr test` performs one complete operation: it
validates the manifest, packs the built folder deterministically, requires the
entry document and declared cover, computes SHA-512 integrity, creates or
reuses the immutable revision, uploads the bytes, and runs publication checks.
The command is deliberately one-pass: that invocation owns the bundle from
local validation through the final check result, with no separate authoring
phase or out-of-band frontend handoff.

Success has process code `0` and the message `Publication checks completed.`
The structured result includes the submission, revision, snapshot digest,
bundle integrity, visibility, channel, and check result. `unlisted` and
`preview` are deliberate safe defaults for a first submission.

## 7. Send the checked bundle for review

```sh
npx @al-amr/cli@0.1.0-alpha.7 publish --json --yes
npx @al-amr/cli@0.1.0-alpha.7 status --json
```

A successful submit reports `Publication submission sent for review.`
`status` returns Registry project state and the linked local submission.
Review remains bound to the frozen manifest snapshot and uploaded bundle
integrity. There is no publisher-hosted preview, well-known handshake, or
Environment OAuth callback in this flow. Published revisions are immutable;
changed bytes require a new revision and a bumped `entry.version`.

## Common failures

- **`No Al-Amr manifest found`** — run the command from `my-world` or pass
  `--cwd`.
- **`No built folder with an index.html`** — run `npm run build`, or pass the
  actual output directory with `--bundle <dir>`.
- **The bundle cover is missing** — keep
  `public/environment-cover.jpg` and ensure the built archive contains the
  manifest's `portal.art.imageUrl` at its root-relative path.
- **The desktop link does not open** — install or start the Al-Amr desktop
  client, then open the `data.link` returned by `alamr dev` again.
- **An immutable bundle conflict is reported** — bump `entry.version`, rebuild,
  and run `alamr test` again; never replace bytes already bound to a revision.
- **Registry network failure** — check the CLI `--registry` value and the
  Registry saved in `.al-amr/project.json`.
- **Linked to a different Registry** — use the Registry recorded in
  `.al-amr/project.json` or deliberately relink the correct owned Project.

## Security checklist

- Do not put a client secret, project token, access token, ID token, or refresh
  token in bundle code or `VITE_*` configuration. The Shell owns user auth.
- Keep executable code and runtime assets inside the bundle; the desktop host
  injects the product CSP and the bundle cannot widen it.
- Treat every frontend Plugin as trusted host code and inspect it before
  installation.
- Use `alamr add`, `update`, and `remove` for Plugin locks; do not hand-edit
  `al-amr.lock.json`.

Next, [add the Player Rig Plugin](/docs/start/add-plugin). For lower-level package
boundaries read the [SDK reference](/reference/sdk), and for authorization
guarantees read [Trust and security](/docs/security).
