> For the complete documentation index, see [llms.txt](https://docs.internetobject.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.internetobject.org/schema-definition-language/advanced-schema-concepts/dynamic-schema.md).

# Open & Dynamic Schemas

By default a schema is **closed**: a record may contain only the declared fields. Adding a `*` marker makes the schema **open**, allowing extra fields beyond those declared.

## Closed by default

Extra values in a record are rejected unless the schema opts in with `*`:

```ruby
~ $schema: { name: string, age: int }
---
~ John, 30                # ✓
~ Alex, 25, extra1        # ✗ additional-values-not-allowed
```

## Allowing extra fields with `*`

Place `*` after the declared fields to accept extras. Positional extras are keyed by index; keyed extras keep their names:

```ruby
~ $schema: { name: string, age: int, * }
---
~ John, 30                       # ✓
~ Alex, 25, Male, cool           # ✓ extras at index 2 and 3
~ { Mia, 28, role: dev }         # ✓ extra keyed field "role"
```

### `*` bare is grammar; `"*"` quoted is a name

The `*` above is **bare**, and that is what makes it the wildcard. Quoted, it is an ordinary member name — the same rule that governs the `?` and `*` [name suffixes](/schema-definition-language/advanced-schema-concepts/memberdef.md#quoted-names-take-the-long-form): quoting says *this name, exactly*.

```ruby
~ $schema: { name: string, * }         # open — accepts any extra field
~ $schema: { name: string, "*": int }  # CLOSED — declares a member called *
```

Data is free to use `*` as a key; JSON-sourced configuration routinely does:

```ruby
~ $schema: { "*": string, admin: string }
---
~ allow, deny            # the member named * holds "allow"
```

A member named `*` does **not** open its schema, and a wildcard is **not** a member: it never appears among the schema's member names, and a schema may carry both at once.

## Typing the extra fields

`*: <type>` constrains every extra field; `*: { <type>, …constraints }` adds constraints:

```ruby
~ $schema: { name: string, *: string }
---
~ { John, role: dev }     # ✓
~ { Alex, code: 123 }     # ✗ not-a-string — extra value must be a string
```

```ruby
~ $schema: { name: string, *: { string, minLen: 4 } }
---
~ { John, dept: Sales }   # ✓
~ { Mia, id: "12" }       # ✗ invalid-min-length — extra is shorter than 4
```

## Map-shaped objects

A wildcard may reference a SchemaDef, which makes `{*: $ref}` the natural schema for **dictionary/map-shaped data** — data whose keys are values in their own right (IDs, codes, locale tags) rather than field names. The keys stay in the data section; the wildcard types every value:

```ruby
~ $question: { questionName: string, points: number }
~ $questions: { *: $question }        # map: ANY key, every value must match $question
~ $schema: { questions: $questions }
---
{ QID1: { Q2, 5 }, QID2: { Q1, 3 } }
```

The wildcard forms, in full:

| Form                         | Meaning                                                |
| ---------------------------- | ------------------------------------------------------ |
| `{ * }` (or `*` listed last) | open — extra members allowed, any type                 |
| `{ *: type }`                | every extra member must match `type`                   |
| `{ *: { … } }`               | every extra member must match the inline SchemaDef     |
| `{ *: [type] }`              | every extra member must be an array of `type`          |
| `{ *: $ref }`                | every extra member must match the referenced SchemaDef |

Declared members and the wildcard compose — `{ id: string, *: number }` requires `id` and lets any other member be a number.

## Dynamic types with anyOf

When a single field must accept more than one type, use `anyOf` (see [Any](/schema-definition-language/data-types/any.md)):

```ruby
test: { any, anyOf: [string, number] }
---
~ One     # ✓
~ 1       # ✓
~ Two     # ✓
```

## When to use curly braces

A top-level schema needs braces only when wrapping a nested object. A nested field with more than one member MUST be enclosed in `{ … }`:

```ruby
# 'address' captures only 'street'; city/state become separate fields — usually not intended
name, age, address: street, city, state, isActive
```

```ruby
# 'address' is a nested object with three members
name, age, address: { street, city, state }, isActive
```

## See Also

* [Internet Object Schema](/schema-definition-language/internet-object-schema.md) · [Any](/schema-definition-language/data-types/any.md)
* [Object (SchemaDef)](/schema-definition-language/data-types/object.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.internetobject.org/schema-definition-language/advanced-schema-concepts/dynamic-schema.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
