Repository: ariga/atlas
Pull request #3126 (2024-09-18 10:32:53)

doc/website: optimize images


Patch:
diff --git a/cmd/atlas/internal/cmdapi/cmdapi_oss.go b/cmd/atlas/internal/cmdapi/cmdapi_oss.go
index fc036111..47002e59 100644
--- a/cmd/atlas/internal/cmdapi/cmdapi_oss.go
+++ b/cmd/atlas/internal/cmdapi/cmdapi_oss.go
@@ -40,6 +40,8 @@ func init() {
 		schemaFmtCmd(),
 		schemaInspectCmd(),
 		unsupportedCommand("schema", "test"),
+		unsupportedCommand("schema", "plan"),
+		unsupportedCommand("schema", "push"),
 	)
 	Root.AddCommand(schemaCmd)
 	migrateCmd := migrateCmd()
@@ -428,10 +430,20 @@ func schemaApplyRun(cmd *cobra.Command, flags schemaApplyFlags, env *Env) error
 
 // applySchemaClean is the community-version of the 'atlas schema clean' handler.
 func applySchemaClean(cmd *cobra.Command, client *sqlclient.Client, drop []schema.Change, flags schemaCleanFlags) error {
+	if flags.dryRun {
+		return AbortErrorf(unsupportedMessage("schema", "clean --dry-run"))
+	}
+	if flags.logFormat != "" {
+		return AbortErrorf(unsupportedMessage("schema", "clean --format"))
+	}
+	if len(drop) == 0 {
+		cmd.Println("Nothing to drop")
+		return nil
+	}
 	if err := summary(cmd, client, drop, cmdlog.SchemaPlanTemplate); err != nil {
 		return err
 	}
-	if flags.AutoApprove || promptUser(cmd) {
+	if flags.autoApprove || promptUser(cmd) {
 		if err := client.ApplyChanges(cmd.Context(), drop); err != nil {
 			return err
 		}
diff --git a/cmd/atlas/internal/cmdapi/project.go b/cmd/atlas/internal/cmdapi/project.go
index 455d74d7..32f292a5 100644
--- a/cmd/atlas/internal/cmdapi/project.go
+++ b/cmd/atlas/internal/cmdapi/project.go
@@ -184,12 +184,16 @@ type (
 			Diff string `spec:"diff"`
 		} `spec:"migrate"`
 		Schema struct {
+			// Clean configures the formatting for 'schema clean'.
+			Clean string `spec:"clean"`
 			// Inspect configures the formatting for 'schema inspect'.
 			Inspect string `spec:"inspect"`
 			// Apply configures the formatting for 'schema apply'.
 			Apply string `spec:"apply"`
 			// Apply configures the formatting for 'schema diff'.
 			Diff string `spec:"diff"`
+			// Push configures the formatting for 'schema push'.
+			Push string `spec:"push"`
 		} `spec:"schema"`
 		schemahcl.DefaultExtension
 	}
@@ -245,6 +249,25 @@ func (e *Env) VarFromURL(s string) (string, error) {
 		sv = e.URL
 	case "dev":
 		sv = e.DevURL
+	case "src":
+		switch n := len(e.Schemas); {
+		case n == 0:
+			return "", fmt.Errorf("env://%s: no schema defined in env %q", u.Host, e.Name)
+		case n > 1:
+			return "", fmt.Errorf("env://%s: expect one schema in env %q, got %d", u.Host, e.Name, n)
+		case n == 1:
+			sv = e.Schemas[0]
+		}
+	case "schema.src":
+		if e.Schema == nil || e.Schema.Src == "" {
+			return "", fmt.Errorf("env://%s: no schema defined in env %q", u.Host, e.Name)
+		}
+		sv = e.Schema.Src
+	case "migration.dir":
+		if e.Migration == nil || e.Migration.Dir == "" {
+			return "", fmt.Errorf("env://%s: no migration dir defined in env %q", u.Host, e.Name)
+		}
+		sv = e.Migration.Dir
 	default:
 		attr, ok := e.Attr(u.Host)
 		if !ok {
diff --git a/cmd/atlas/internal/cmdapi/schema.go b/cmd/atlas/internal/cmdapi/schema.go
index ac8ebd88..c3f31613 100644
--- a/cmd/atlas/internal/cmdapi/schema.go
+++ b/cmd/atlas/internal/cmdapi/schema.go
@@ -166,8 +166,10 @@ func planOptions(c *sqlclient.Client) []migrate.PlanOption {
 }
 
 type schemaCleanFlags struct {
-	URL         string // URL of database to apply the changes on.
-	AutoApprove bool   // Don't prompt for approval before applying SQL.
+	url         string // URL of database to apply the changes on.
+	autoApprove bool   // Don't prompt for approval before applying SQL.
+	logFormat   string // Log format.
+	dryRun      bool   // Only show SQL on screen instead of applying it.
 }
 
 // schemaCleanCmd represents the 'atlas schema clean' subcommand.
@@ -190,15 +192,16 @@ As a safety feature, 'atlas schema clean' will ask for confirmation before attem
 		}
 	)
 	cmd.Flags().SortFlags = false
-	addFlagURL(cmd.Flags(), &flags.URL)
-	addFlagAutoApprove(cmd.Flags(), &flags.AutoApprove)
+	addFlagURL(cmd.Flags(), &flags.url)
+	addFlagDryRun(cmd.Flags(), &flags.dryRun)
+	addFlagFormat(cmd.Flags(), &flags.logFormat)
+	addFlagAutoApprove(cmd.Flags(), &flags.autoApprove)
 	cobra.CheckErr(cmd.MarkFlagRequired(flagURL))
 	return cmd
 }
 
 func schemaCleanRun(cmd *cobra.Command, _ []string, flags schemaCleanFlags) error {
-	// Open a client to the database.
-	c, err := sqlclient.Open(cmd.Context(), flags.URL)
+	c, err := sqlclient.Open(cmd.Context(), flags.url)
 	if err != nil {
 		return err
 	}
@@ -225,10 +228,6 @@ func schemaCleanRun(cmd *cobra.Command, _ []string, flags schemaCleanFlags) erro
 			return err
 		}
 	}
-	if len(drop) == 0 {
-		cmd.Println("Nothing to drop")
-		return nil
-	}
 	return applySchemaClean(cmd, c, drop, flags)
 }
 
@@ -552,6 +551,9 @@ func setSchemaEnvFlags(cmd *cobra.Command, env *Env) error {
 		if err := maySetFlag(cmd, flagURL, env.URL); err != nil {
 			return err
 		}
+		if err := maySetFlag(cmd, flagFormat, env.Format.Schema.Clean); err != nil {
+			return err
+		}
 	case "inspect":
 		if err := maySetFlag(cmd, flagURL, env.URL); err != nil {
 			return err
@@ -574,6 +576,9 @@ func setSchemaEnvFlags(cmd *cobra.Command, env *Env) error {
 		if err := maySetFlag(cmd, flagURL, strings.Join(srcs, ",")); err != nil {
 			return err
 		}
+		if err := maySetFlag(cmd, flagFormat, env.Format.Schema.Push); err != nil {
+			return err
+		}
 	case "test":
 		// Give the "src" precedence over the "url" argument.
 		if len(srcs) > 0 {
diff --git a/doc/md/atlas-vs-others.mdx b/doc/md/atlas-vs-others.mdx
index d0bb7ffa..0d81dc28 100644
--- a/doc/md/atlas-vs-others.mdx
+++ b/doc/md/atlas-vs-others.mdx
@@ -65,7 +65,7 @@ ORM tend to provide native programming language based DSLs for defining migratio
 SQL.
 
 ORMs tend to be community-based projects without a commercial entity backing them (with the exception of
-[Prisma](https://prisma.io)).  This means that the level of support and maintenance can vary greatly between different
+Prisma.  This means that the level of support and maintenance can vary greatly between different
 options.
 
 **Atlas** - is a database schema-as-code tool that applies modern DevOps principles to the ancient art of database
@@ -126,7 +126,7 @@ one needs to replay all migrations on an empty database and inspect the result.
 **ORMs and frameworks** are more difficult to classify. On one hand, ORMs and frameworks revolve around a
 code-first definition of the different entities in the application data model.
 
-On the other hand, migrations are defined as revisions, in a version-based approach. Aside from [Prisma](https://prisma.io)
+On the other hand, migrations are defined as revisions, in a version-based approach. Aside from Prisma
 and [Django](https://www.djangoproject.com/), most frameworks do not supply a mechanism to ensure that the planned
 migrations are consistent with the application data model as it is defined in the ORM.
 
diff --git a/doc/md/cloud/features/registry.mdx b/doc/md/cloud/features/registry.mdx
index 3e425957..b3ba6095 100644
--- a/doc/md/cloud/features/registry.mdx
+++ b/doc/md/cloud/features/registry.mdx
@@ -12,9 +12,8 @@ migrations.
 
 ### Pushing a Schema
 
-Push your schema definition to the Atlas Registry by using the `atlas schema push` command. This command uploads your
-schema to the registry and make it available to others in your organization. A new schema version is created if the
-schema definition is different from the previous version:
+The `atlas schema push` command allows you to push your schema definition to the Atlas Registry. If no repository exists
+in the registry for the schema, a new one is created. Otherwise, a new version is generated.
 
 ```bash {4}
 atlas schema push myapp \
@@ -23,15 +22,39 @@ atlas schema push myapp \
 https://example.atlasgo.cloud/schemas/141733920769
 ```
 
+Once pushed, the schema is available to others in your organization and can be referenced in your deployment pipelines.
+
 <div style={{textAlign: 'center'}}>
     <img src="https://atlasgo.io/uploads/cloud/images/schema-overview-v1.png" alt="atlas schema push" width="100%"/>
     <p style={{fontSize: 12}}>Schema created with <code>atlas schema push</code></p>
 </div>
 
+#### Registry Config for Schemas
+
+The example below demonstrates how to configure the registry for your schema within an `env` block. All commands
+executed in this environment will automatically use the configured repository:
+
+```hcl title="atlas.hcl" {6-13}
+env "local" {
+  # URL to the underlying database.
+  url = env("DATABASE_URL")
+  # URL to the dev-database.
+  dev = "sqlite://dev?mode=memory"
+  schema {
+    # Desired schema state.
+    src = "file://schema.sql"
+    # Atlas Registry config.
+    repo {
+      name = "myapp"
+    }
+  }
+}
+```
+
 ### Pushing a Migration Directory
 
-Push your migration directory to the registry by using the `atlas migrate push` command. This will upload your
-migration files to the registry and make the directory available to others in your organization:
+The `atlas migrate push` command allows you to push your migration directory to the Atlas Registry. If no repository
+exists in the registry for the pushed directory, a new one is created. Otherwise, the directory state will be updated.
 
 ```bash {4}
 atlas migrate push myapp \
@@ -40,6 +63,9 @@ atlas migrate push myapp \
 https://example.atlasgo.cloud/dirs/4294967370
 ```
 
+Once pushed, the migration directory is available to others in your organization and can be referenced in your deployment
+pipelines.
+
 Note that Atlas Registry is schema-aware. In addition to storing and versioning your migrations, it provides ER diagrams,
 SQL diffing, [schema docs](/cloud/features/schema-docs), and other capabilities that make working with your
 schema easier.
@@ -49,6 +75,28 @@ schema easier.
     <p style={{fontSize: 12}}>Migration Directory created with <code>atlas migrate push</code></p>
 </div>
 
+#### Registry Config for Migration Directories
+
+The example below demonstrates how to configure the registry for your migration directory within an `env` block.
+All commands executed in this environment will automatically use the configured repository:
+
+```hcl title="atlas.hcl" {8-13}
+env "local" {
+  # URL to the underlying database.
+  url = env("DATABASE_URL")
+  # URL to the dev-database.
+  dev = "sqlite://dev?mode=memory"
+  # URL to the desired schema state.
+  src = "file://schema.sql"
+  migration {
+    # Atlas Registry config.
+    repo {
+      name = "myapp"
+    }
+  }
+}
+```
+
 ## Simplifying Deployments
 
 When you push a migration directory to the Schema Registry, you can give it a tag in the same way that you would tag a
diff --git a/doc/md/community-edition.mdx b/doc/md/community-edition.mdx
index aa296d21..030ea83d 100644
--- a/doc/md/community-edition.mdx
+++ b/doc/md/community-edition.mdx
@@ -9,8 +9,7 @@ import TabItem from '@theme/TabItem';
 ## Obtaining Community Binaries
 
 Users that want to use the Community Edition of Atlas which is built directly from the
-[Atlas GitHub repository](https://github.com/ariga/atlas) and licensed under the
-Apache 2.0 license can do so by following the instructions below.
+[Atlas GitHub repository](https://github.com/ariga/atlas) and licensed under the Apache 2.0 license can do so by following the instructions below.
 
 <Tabs
     defaultValue="apple-intel-plain+linux"
@@ -81,7 +80,7 @@ open-source codebase with some additional proprietary features.  Usage of the Op
 
 ### Features Unavailable in the Community Edition
 
-Some commands are only available in the Open and Pro Editions:
+The Community Edition does not support the following commands and integrations:
 
 #### Versioned Migrations
 
@@ -90,14 +89,24 @@ Some commands are only available in the Open and Pro Editions:
 - `migrate rebase` - Reverting applied migration files from the database.
 - `migrate edit` - Edit a migration file.
 - `migrate rm` - Remove a migration file.
-- `migrate push` - Push the migration files to the Schema Registry.
+- `migrate push` - Push the migration files to [Atlas Registry](/cloud/features/registry).
 - `migrate test` - Run tests using the Atlas Testing Framework.
 
-To use these features, please use the Open or Pro Edition of Atlas.
-
 #### Declarative Migrations
 
 - `schema test` - Run tests using the Atlas Testing Framework.
+- `schema plan` - [Pre-plan declarative migrations](/declarative/plan).
+- `schema push` - Push a new version to [Atlas Registry](/cloud/features/registry).
+
+#### Integrations and Providers
+
+- [Atlas Kubernetes Operator](https://github.com/ariga/atlas-operator)
+- [Atlas Terraform Provider](https://github.com/ariga/terraform-provider-atlas)
+- [Atlas GitHub Action](https://github.com/ariga/atlas-action)
+- [Atlas CircleCI Orb](https://github.com/ariga/atlas-orb)
+- [Atlas JetBrains Plugin](https://plugins.jetbrains.com/plugin/22307-atlas-hcl)
+- [Atlas VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Ariga.atlas-hcl)
+- [Atlas Go SDK](https://github.com/ariga/atlas-go-sdk)
 
 #### Database Features
 
@@ -111,6 +120,7 @@ The following database features are not supported in the Community Edition:
 - Domain types
 - Extensions
 - Sequences
+- Composite types
 
 #### Drivers
 
diff --git a/doc/md/concepts/workflows.md b/doc/md/concepts/workflows.md
index 2d472fc7..e7029b51 100644
--- a/doc/md/concepts/workflows.md
+++ b/doc/md/concepts/workflows.md
@@ -79,7 +79,7 @@ prefer a more imperative approach where each change to the database schema is ch
 to source control and reviewed during code-review.  Each such change
 is called a "migration", as it migrates the database schema from the previous version to 
 the next. To support this kind of requirement, many popular database schema management
-tools such as [Flyway](https://flywaydb.org/), [Liquibase](https://liquibase.org/) or 
+tools such as [Flyway](https://atlasgo.io/atlas-vs-others), [Liquibase](https://atlasgo.io/atlas-vs-others) or 
 [golang-migrate](https://github.com/golang-migrate/migrate) support a workflow that
 is commonly called "versioned migrations".
 
diff --git a/doc/md/declarative/apply.mdx b/doc/md/declarative/apply.mdx
index ae390252..e14bc9bd 100644
--- a/doc/md/declarative/apply.mdx
+++ b/doc/md/declarative/apply.mdx
@@ -10,18 +10,18 @@ import TabItem from '@theme/TabItem';
 With Atlas, users do not need to plan database schema changes themselves. Instead
 of figuring out the correct SQL statements to get their database to the desired state,
 Atlas supports a kind of workflow that we call _declarative schema migration_.
-With declarative schema migrations the user provides a connection string to the target database
+With declarative schema migrations the user provides a URL (connection string) to the target database
 and the desired schema, while Atlas does the planning.
 
 [Read more about declarative workflows](/concepts/declarative-vs-versioned)
 
 :::info Login to Atlas
-By default, running `atlas schema apply` applies only **schemas**, **tables**, and their associated **indexes** and
+By default, the `atlas schema apply` command manages only **schemas**, **tables**, and their associated **indexes** and
 constraints such as **foreign keys** and **checks**.
 
 **Views**, **materialized views**, **functions**, **procedures**, **triggers**, **sequences**, **domains**, **extensions**,
-and additional beta features are available to [Atlas Pro users](/features#pro). To include these resources in schema migrations, use
-the following command:
+and additional database features are available to [Atlas Pro users](/features#pro). To include these resources in schema migrations,
+use the following command:
 
 ```shell
 atlas login
@@ -30,8 +30,9 @@ atlas login
 
 ## Flags
 
-The `schema apply` command generates a migration plan and applies it to the database to bring it to the desired state.
-A desired state can be specified using a database URL, an HCL or SQL schema, or a migration directory.
+The `schema apply` command auto-generates a migration plan and applies it to the database to bring it to the desired state.
+The desired state can be defined using an [HCL](/atlas-schema/hcl) or [SQL schema](/atlas-schema/sql) definition, a database [URL](../concepts/url), or an
+external schemas like [ORM](/atlas-schema/external).
 
 * `--url` (`-u` accepted as well) - the [URL](/concepts/url) of the database to be inspected.
 * `--to` - a list of [URLs](/concepts/url) to the desired state: can be a database URL, an HCL or
@@ -40,15 +41,98 @@ A desired state can be specified using a database URL, an HCL or SQL schema, or
 * `--schema` (optional, may be supplied multiple times) - schemas to inspect within the target database.
 * `--exclude` (optional, may be supplied multiple times) - filter out resources matching the given glob pattern.
 * `--format` (optional) - [Go template](https://pkg.go.dev/text/template) to use to format the output.
+* `--edit` (optional) - open the planned migration in the default editor, allowing the user to modify it.
 
-### Auto-approval
+## Approval Policy
+
+The `schema apply` command requires user review and approval before executing the migration against the target database.
+The approval process can occur during migration planning (locally or in CI), automatically in the applying stage using
+the linting review policy, or be completely skipped, which is not recommended in production environments. Let's cover
+all options:
+
+1. **Interactive Review** (default): The `atlas schema apply` command will print the SQL statements it is going to run
+   and prompt the user for approval. Users can review the migration plan and either approve or reject it. [Atlas Pro](/features#pro)
+   users can set the `--dev-url` flag to run analysis and simulation on the [_Dev-Database_](../concepts/dev.mdx) of the
+   proposed changes, and get a detailed linting report when reviewing the migration.
+
+2. **Skip review** (auto-approval): The `--auto-approve` flag can be used to skip the review process and automatically
+   apply the migration to the target database. Although this option is convenient for experimentation and development,
+   it is not recommended for production environments, as it may lead to unexpected changes, such as destructive operations.
+
+3. **Approve using Review Policy**: Atlas ships with an analysis engine that can detect the impact of proposed changes
+   to the target database. For example, Atlas can detect irreversible destructive changes that will result in data
+   loss or data-dependent changes that may fail due to data integrity constraints.
+
+   Users can configure Atlas to automatically approve migrations that pass the analysis engine checks (for example, no
+   destructive changes were detected) and require manual review for migrations that fail the checks. Read more on how to
+   configure the [Review Policy](#review-policy) for your project.
+
+4. **Pre-plan (and Approve) Migrations**: The `atlas schema plan` command allows users to pre-plan, review, and approve
+   migrations before executing `atlas schema apply` on the database. This enables users to preview and modify SQL changes,
+   involve team members in the review process, and ensure that no human intervention is required during the `atlas schema apply` phase.
+   [Read more about `atlas schema plan`](/declarative/plan) and how to integrate it into your CI/CD pipeline.
+
+## Review Policy
+
+Users can define in which cases their schema changes require manual review and approval, if no migration has already been
+planned and approved for the given [_Schema Transition_ (State1 -> State2)](/declarative/plan#overview). How does it work?
+
+1. During `atlas schema apply`, Atlas checks if there is an approved pre-planned migration for the given schema transition
+   (State1 -> State2). If there is one, Atlas will apply it without requiring manual review.
+2. If no migration has been pre-planned, Atlas will run analysis and simulation on the [_Dev-Database_](../concepts/dev.mdx)
+   to detect the impact of the proposed changes.
+3. Atlas then prints the linting report, and based on the review policy defined in the `atlas.hcl` file, it will
+   decide whether to auto-approve the migration or require manual review.
+
+The review policy can be set to one of the following values:
+
+1. `ERROR` - Atlas will require manual review and approval only if the linting report contains errors, i.e., one of the
+   analyzers is configured to return an error. See the [destructive-changes analyzer](/lint/analyzers#destructive-changes)
+   as an example.
+2. `WARNING` - Atlas will require manual review and approval if the linting report contains warnings (diagnostics) or
+   errors. See the [list of checks](/lint/analyzers#checks) that can be detected by the analyzers.
+3. `ALWAYS` (default) - Atlas will always require manual review and approval, regardless of the linting report.
+
+<Tabs>
+<TabItem value={"Global Configuration"}>
+
+```hcl title="atlas.hcl" {2}
+lint {
+  review = ERROR // ERROR | ALWAYS
+  destructive {
+    error = false
+  }
+}
+```
+</TabItem>
+<TabItem value={"Env Configuration"}>
+
+```hcl title="atlas.hcl" {3}
+env "prod" {
+  lint {
+    review = ERROR // ERROR | ALWAYS
+    destructive {
+      error = false
+    }
+  }
+}
+```
+</TabItem>
+</Tabs>
+
+The typical workflow for applying schema changes without manual review is to use `atlas schema plan` to pre-plan the
+migration and falling back to the "review policy" for schema transitions that were not pre-planned but are still safe
+to apply. The `atlas schema plan` command can be integrated into the CI/CD pipeline to ensure that all schema changes
+are reviewed and approved before being applied to the database.
+
+## Auto-approval
 
 Before executing the migration against the target database, Atlas will print the SQL
 statements that it is going to run and prompt the user for approval. Users that wish
 to automatically approve may run the `schema apply` command with the `--auto-approve`
 flag.
 
-### Dry-runs
+## Dry-runs
 
 In order to skip the execution of the SQL queries against the target database,
 users may provide the `--dry-run` flag. When invoked with this flag, Atlas will
@@ -56,7 +140,7 @@ connect to the target database, inspect its current state, calculate the diff
 between the provided desired schema and print out a series of SQL statements to
 reconcile any gaps between the inspected and desired schemas.
 
-### Dev-database
+## Dev-database
 
 When storing schema definitions, many database engines perform some form of
 normalization. That is, despite us providing a specific definition of some
diff --git a/doc/md/declarative/inspect.mdx b/doc/md/declarative/inspect.mdx
index 09743d9e..410307c9 100644
--- a/doc/md/declarative/inspect.mdx
+++ b/doc/md/declarative/inspect.mdx
@@ -22,8 +22,8 @@ By default, running `atlas schema inspect` inspects only **schemas**, **tables**
 constraints such as **foreign keys** and **checks**.
 
 **Views**, **materialized views**, **functions**, **procedures**, **triggers**, **sequences**, **domains**, **extensions**,
-and additional features are available to [Atlas Pro users](/features#pro). To include these resources in the inspection, use the
- following command:
+and additional database features are available to [Atlas Pro users](/features#pro). To include these resources in the inspection,
+use the following command:
 
 ```shell
 atlas login
@@ -375,7 +375,7 @@ atlas schema inspect -u '<url>' -w
 
 <p style={{textAlign: "center"}}>
     <a href="https://gh.atlasgo.cloud/explore/40d83919">
-        <img src="https://atlasgo.io/uploads/erd-example.png" alt="Schema ERD"/>
+        <img src="https://atlasgo.io/uploads/cloud/images/erd-example-v1.png" alt="Schema ERD"/>
     </a>
 </p>
 
diff --git a/doc/md/declarative/plan.mdx b/doc/md/declarative/plan.mdx
new file mode 100644
index 00000000..5f6ecca0
--- /dev/null
+++ b/doc/md/declarative/plan.mdx
@@ -0,0 +1,664 @@
+---
+id: plan
+slug: /declarative/plan
+title: Pre-planning Schema Migrations
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The `atlas schema plan` command allows users to pre-plan declarative schema migrations before applying them to the
+database. Once a migration is planned, reviewed, and approved, it can be applied using the [`atlas schema apply`](./apply.mdx)
+command to update the database to the desired state.
+
+Note: If you are not familiar with the `atlas schema apply` command, please refer to the [Applying Changes](./apply.mdx)
+guide first.
+
+:::info [Atlas Pro Feature](/features#pro)
+The `atlas schema plan` command is available exclusively to Pro users. To use this feature, run:
+```
+atlas login
+```
+:::
+
+### Overview
+
+The `atlas schema apply` command updates the database to the desired state defined by the user. These auto-planned schema
+changes can be approved in one of the following ways:
+
+1. Reviewed and approved interactively by a human.
+2. Auto-approved using the `--auto-approve` flag, though this may be risky in a production database.
+3. Auto-reviewed based on the lint-review policy, which requires human review only if the linter detects issues. or errors.
+
+These options depend on the database state and cannot predict whether the migration will succeed, fail, or abort.
+This is where `atlas schema plan` becomes useful.
+
+The `atlas schema plan` command allows users to pre-plan, review, and approve migrations before executing `atlas schema apply`
+on the database. This enables users to preview and modify SQL changes, involve team members in the review process, and ensure
+that no human intervention is required during the `atlas schema apply` phase.
+
+How does it work? In short (more details below), `atlas schema plan` generates a migration plan for the specified
+_Schema Transition_ (State1 -> State2) and stores it in the [Atlas Registry](/cloud/features/registry). During
+`atlas schema apply`, Atlas checks if there is an approved migration plan for the specific schema transition and applies
+it without recalculating SQL changes at runtime or requiring user-approval.
+
+If users wish to modify the auto-generated migration plan, they can edit it locally and then push it to the Atlas Registry.
+
+### Local Example
+
+Let's consider a simple example. We have a table `users` with two columns `id` and `name`, and we want to add a new column
+`email` to the table.
+
+#### Example Setup
+
+Before running `atlas schema plan`, we need to ensure that the schema repository exists in Atlas Registry and there
+is a database containing the previous schema state (before our changes):
+
+<Tabs>
+<TabItem value={"Schema Definition"}>
+
+```sql title="schema.sql"
+CREATE TABLE users (
+  id INTEGER PRIMARY KEY AUTOINCREMENT,
+  name TEXT
+);
+```
+</TabItem>
+<TabItem value={"Config File"}>
+
+```hcl title="atlas.hcl"
+env "local" {
+  # URL to the underlying database.
+  url = "sqlite://main.db"
+  # URL to the dev-database.
+  dev = "sqlite://dev?mode=memory"
+  schema {
+    # Desired schema state.
+    src = "file://schema.sql"
+    # Atlas Registry config.
+    repo {
+      name = "app"
+    }
+  }
+}
+```
+</TabItem>
+</Tabs>
+
+We run `atlas schema push` to create the schema in Atlas Registry:
+
+```planlog {3-5}
+atlas schema push --env local
+
+Schema: app
+  -- Atlas URL: atlas://app
+  -- Cloud URL: https://a8m.atlasgo.cloud/schemas/141733920781
+```
+
+Then, we run `atlas schema apply` to align the database with the schema state:
+
+```bash
+atlas schema apply --env local --auto-approve
+```
+
+#### Changing the Schema
+
+At this stage, we want to add a non-nullable `email` column to the `users` table. Let's update the `schema.sql` file and then run
+`atlas schema plan` to generate a migration plan.
+
+```sql title="schema.sql"
+CREATE TABLE users (
+  id INTEGER PRIMARY KEY AUTOINCREMENT,
+  name TEXT,
+//highlight-next-line-info
+  email TEXT NOT NULL
+);
+```
+
+We run `atlas schema plan` to generate a migration plan for adding the `email` column to the `users` table:
+```bash
+atlas schema plan --env local
+```
+The output looks like this:
+```planlog {10-13}
+Planning migration from local database to file://schema.sql (1 statement in total):
+
+  -- add column "email" to table: "users":
+    -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL;
+
+-------------------------------------------
+
+Analyzing planned statements (1 in total):
+
+  -- data dependent changes detected:
+    -- L2: Adding a non-nullable "text" column "email" will fail in case table "users"
+       is not empty https://atlasgo.io/lint/analyzers#MF103
+  -- ok (346.192µs)
+
+  -------------------------
+  -- 5.038728ms
+  -- 1 schema change
+  -- 1 diagnostic
+? Approve or abort the plan:
+  ▸ Approve and push
+    Abort
+```
+
+#### Data-Dependent Changes
+
+Atlas detects data-dependent changes in the migration plan and provides a diagnostic message. In this case, it warns
+that adding the non-nullable `email` column, will fail if the `users` table is not empty. The recommended solution is to
+provide a default value for the new column. Let's fix this by adding a default value to the `email` column and re-run the
+`atlas schema plan` command.
+
+```sql title="schema.sql"
+CREATE TABLE users (
+  id INTEGER PRIMARY KEY AUTOINCREMENT,
+  name TEXT,
+//highlight-next-line-info
+  email TEXT NOT NULL DEFAULT 'unknown'
+);
+```
+
+Then, we run `atlas schema plan` again to generate a new migration plan, but this time, we approve it:
+
+```bash
+atlas schema plan --env local
+```
+
+```planlog
+Planning migration from local database to file://schema.sql (1 statement in total):
+
+  -- add column "email" to table: "users":
+    -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+
+-------------------------------------------
+
+Analyzing planned statements (1 in total):
+
+  -- no diagnostics found
+
+  -------------------------
+  -- 6.393773ms
+  -- 1 schema change
+? Approve or abort the plan:
+  ▸ Approve and push
+    Abort
+```
+
+Once approved, the migration plan will be pushed to the Atlas Registry, and can be applied using `atlas schema apply`.
+
+```planlog
+Plan Status: APPROVED
+  -- Atlas URL: atlas://app/plans/20240923085308
+  -- Cloud URL: https://a8m.atlasgo.cloud/schemas/141733920769/plans/210453397504
+```
+
+At this stage, we can run `atlas schema apply` to apply the changes to the database, on any environment, without
+re-calculating the SQL changes at runtime or requiring human intervention.
+
+```applylog
+Applying approved migration using pre-planned file 20240923085308 (1 statement in total):
+
+  -- add column "email" to table: "users"
+    -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+  -- ok (749.815µs)
+
+  -------------------------
+  -- 802.902µs
+  -- 1 migration
+  -- 1 sql statement
+```
+
+### Atlas Registry
+
+Atlas Registry allows you to store, version, and maintain a single source of truth for your database schemas and its
+migration plans. It is similar to Docker Hub, but for your schemas and migrations. In addition to functioning as storage,
+it is schema-aware and provides extra capabilities such as ER diagrams, SQL diffing, [schema docs](/cloud/features/schema-docs),
+and more.
+
+<div style={{textAlign: 'center'}}>
+    <img src="https://atlasgo.io/uploads/cloud/images/schema-overview-v1.png" alt="atlas migrate push" width="100%"/>
+    <p style={{fontSize: 12}}>Schema pushed with <code>atlas schema push</code></p>
+</div>
+
+### Edit a Plan
+
+One of the first questions that come to mind when comparing the declarative approach to the versioned approach is:
+_How can I edit a migration plan?_ There are three ways to edit a migration plan:
+
+1. **Edit in-place:**
+   - Ensure the `EDITOR` environment variable is set (e.g., `export EDITOR=vim`).
+   - Run `atlas schema plan --edit` to open the plan in the default editor. Upon closing, the plan will be pushed
+     after approval.
+
+2. **Save, edit, and push:**
+   - Run `atlas schema plan --save` to save the plan to a file and edit it manually.
+   - Run `atlas schema plan push --file file://<path>` to push the edited plan to the Atlas Registry.
+
+3. **Pull, edit, and push:**
+   - Pull a remote plan by running `atlas schema plan pull --url atlas://<schema>/plans/<plan> > name.plan.hcl`.
+   - Open `name.plan.hcl` in the editor, and edit the `migration` attribute.
+   - Push the edited plan to the Atlas Registry by running `atlas schema plan push --file file://<path>`.
+
+To complete the example, let's edit the migration plan from the [example above](#local-example) by changing all `email`
+columns with `'unknown'` value to a computed email value:
+
+1. We pull the plan first into a file named `20240923085308.plan.hcl`:
+
+  ```bash
+  atlas schema plan pull --url atlas://app/plans/20240923085308 > 20240923085308.plan.hcl
+  ```
+
+  ```hcl title="20240923085308.plan.hcl"
+  plan "20240923085308" {
+    from      = "vJYpErjN4kWJpw4nRaJcEX3xx/jExj4a05Ll3Y7gXr4="
+    to        = "B5OVckDEeHcaSdYCUMEfYe8CZN85ahLkef44hfwCe2g="
+    migration = <<-SQL
+    -- Add column "email" to table: "users"
+    ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+    SQL
+  }
+  ```
+
+  Note that the `from` and `to` are fingerprints of the schema states. They are used to identify the states in the
+  schema transition. We will ignore them for now (without changing them, of course) and focus on the migration attribute.
+
+2. We edit the `migration` attribute to change all rows with `'unknown'` emails to a computed email value:
+
+  ```hcl title="20240923085308.plan.hcl"
+  plan "20240916133205" {
+    from      = "vJYpErjN4kWJpw4nRaJcEX3xx/jExj4a05Ll3Y7gXr4="
+    to        = "B5OVckDEeHcaSdYCUMEfYe8CZN85ahLkef44hfwCe2g="
+    migration = <<-SQL
+    -- Add column "email" to table: "users"
+    ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+    //highlight-next-line-info
+    -- Change all unknown "email" columns with a new computed email
+    //highlight-next-line-info
+    UPDATE `users` SET `email` = PRINTF('%s+a8m@atlasgo.cloud', `name`) WHERE `email` = 'unknown';
+    SQL
+  }
+  ```
+
+3. Then, we push the edited plan to the Atlas Registry:
+
+  ```bash
+  atlas schema plan push --file file://20240923085308.plan.hcl
+  ```
+
+  ```planlog
+  Planning migration statements (2 in total):
+
+    -- add column "email" to table: "users":
+      -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+    -- change all unknown "email" columns with a new computed email
+      -> UPDATE `users` SET `email` = PRINTF('%s+a8m@atlasgo.cloud', `name`) WHERE `email` = 'unknown';
+
+  -------------------------------------------
+
+  Analyzing planned statements (2 in total):
+
+    -- no diagnostics found
+
+    -------------------------
+    -- 43.566575ms
+    -- 2 schema changes
+  ? Approve or abort the plan:
+    ▸ Approve and push
+      Abort
+  ```
+  Once approved, the migration plan will be pushed to the Atlas Registry.
+
+  :::note Schema Drift
+  Note that if your manual changes are not in sync with the desired state (i.e., do not bring the database to the
+  desired state), Atlas will detect the schema drift and reject this migration plan.
+  :::
+
+4. Then, running `atlas schema apply` will apply the changes to the database, including the new `UPDATE` statement.
+
+   ```applylog
+   Applying approved migration using pre-planned file 20240923085308 (2 statements in total):
+
+     -- add column "email" to table: "users"
+       -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+     -- ok (826.977µs)
+
+     -- change all unknown "email" columns with a new computed email
+       -> UPDATE `users` SET `email` = PRINTF('%s+a8m@atlasgo.cloud', `name`) WHERE `email` = 'unknown';
+     -- ok (447.152µs)
+
+     -------------------------
+     -- 1.353026ms
+     -- 1 migration
+     -- 2 sql statements
+   ```
+
+### Push a Plan
+
+By default, `atlas schema plan` proposes pushing the plan file to the Atlas registry. However, you can use the `--save` flag
+to dump the plan to a file, edit it, and then push it manually to the Atlas Registry using the `atlas schema plan push`
+command:
+
+```bash
+atlas schema plan push \
+  --file file://<path-plan-file> \
+  --env <config-env>
+```
+
+### Approve a Plan
+
+By default, `atlas schema plan` pushes plans in an **`APPROVED`** state to the registry. However, in some cases, we may prefer to create
+the plan in **pending** state, and later approve it manually or automatically after it passes the team's review.
+
+There are two ways to create a plan in a **pending** state and approve it after review.
+1. Manual workflow:
+   1. Run the `atlas schema plan` command with the `--pending` flag. This creates the plan in **pending** state.
+   2. Then, in order to approve the plan, either go to the Atlas Registry Web UI and approve the plan, or run the
+      `atlas schema plan approve` command.
+2. Automated (CI) workflow:
+   1. When setting the `schema/plan` GitHub Action for your repository, Atlas automatically creates a plan in a pending state.
+   2. Then, after the PR is merged, Atlas auto-approves the created plan in the registry.
+
+:::info Protected Flow
+Users can protect their registry schemas by limiting who can push changes, push approved plans, or approve existing plans.
+To enable this for your schema, go to the schema repository settings in the registry and enable the `Protected Flows` option.
+:::
+
+### Pull a Plan
+
+To pull a plan from the Atlas Registry, use the `atlas schema plan pull` command:
+
+```bash
+atlas schema plan pull \
+  --url atlas://app/plans/add_email > add_email.plan.hcl
+```
+
+### List Plans
+
+To list all plans in the Atlas Registry for the given schema transition, use the `atlas schema plan list` command:
+
+```bash
+atlas schema plan list \
+  --env local
+```
+
+```planlog
+Plan Status: APPROVED
+  -- Atlas URL: atlas://app/plans/add_email
+  -- Cloud URL: https://<tenant>.atlasgo.cloud/schemas/<schema-id>/plans/<plan-id>
+```
+
+### Lint a Plan
+
+To lint a plan (remote or local) before pushing it to the Atlas Registry, use the `atlas schema plan lint` command:
+
+```bash
+atlas schema plan lint \
+  --file file://add_email.plan.hcl \
+  --env local
+```
+
+### Apply a Plan
+
+Running `atlas schema apply` searches for a migration plan in the Atlas Registry and applies it to the database, if exists.
+However, in unusual cases, you might have multiple (approved) migration plans for the same schema transition store in the
+registry (e.g., one per environment). In that case, running `atlas schema apply` will abort with the following error:
+
+```text
+Error: multiple pre-planned migrations were found in the registry for this schema transition.
+
+  Current hash: vJYpErjN4kWJpw4nRaJcEX3xx/jExj4a05Ll3Y7gXr4=
+  Desired hash: hna312Vk535aibL1hTRcBlxeyUvIwV6Mov7kfaZ2+3s=
+
+Plans found:
+
+  atlas://app/plans/<plan-one>
+  atlas://app/plans/<plan-two>
+
+To resolve the issue, either delete the conflicting plans or provide the plan URL explicitly using the --plan flag.
+```
+
+In this case, we either delete the conflicting plans from the Atlas Registry or provide the plan URL explicitly using
+the `--plan` flag:
+
+```applylog
+Applying approved migration using pre-planned file 20240923085308 (2 statements in total):
+
+  -- add column "email" to table: "users"
+    -> ALTER TABLE `users` ADD COLUMN `email` text NOT NULL DEFAULT 'unknown';
+  -- ok (789.621µs)
+
+  -- change all unknown "email" columns with a new computed email
+    -> UPDATE `users` SET `email` = PRINTF('%s+a8m@atlasgo.cloud', `name`) WHERE `email` = 'unknown';
+  -- ok (883.177µs)
+
+  -------------------------
+  -- 1.77283ms
+  -- 1 migration
+  -- 2 sql statements
+```
+
+### GitHub Actions
+
+Atlas provides an [official GitHub Actions integration](https://github.com/ariga/atlas-action) to automatically plan,
+review, and approve declarative schema migrations during PR workflows. The example below demonstrates how to set up this
+workflow for your repository.
+
+<div style={{textAlign: 'center'}}>
+    <img src="https://atlasgo.io/uploads/plan-action.png" alt="GitHub Action for schema plan command" width="100%"/>
+    <p style={{fontSize: 12}}>Plan Generated by <code>atlas schema plan</code></p>
+</div>
+
+#### Create a Schema Repository in Atlas Registry
+
+For the purpose of the example, let's create a schema repository named `demo` in Atlas Registry with the following
+SQL schema:
+
+<Tabs>
+<TabItem value={"Schema Definition"}>
+
+```sql title="schema.sql"
+CREATE TABLE users (
+  id BIGSERIAL PRIMARY KEY
+);
+```
+</TabItem>
+<TabItem value={"Config File"}>
+
+```hcl title="atlas.hcl"
+env "dev" {
+  # The URL to the dev-database.
+  dev = "docker://postgres/15/dev?search_path=public"
+  schema {
+    # Desired schema state.
+    src = "file://schema.sql"
+    # Atlas Registry config.
+    repo {
+      name = "demo"
+    }
+  }
+}
+```
+</TabItem>
+</Tabs>
+
+To create the schema repository in the Atlas Registry, run the following command:
+
+```bash
+atlas schema push --env dev
+
+https://<your-tenant>.atlasgo.cloud/schemas/141733920769
+```
+
+#### Set Up the `schema/push` GitHub Action
+
+In order to keep our schema repository up-to-date with the latest changes, we can set up the `schema/push` GitHub Action. This
+action automatically pushes the schema to the Atlas Registry whenever changes are made to the SQL schema file:
+
+```yaml title=".github/workflows/schema-push.yml"
+name: Push Declarative Schemas
+on:
+  push:
+    branches:
+      - master
+    paths:
+      - .github/workflows/atlas-push.yaml
+      - 'schema.sql' # Can be HCL, ORM, other instead.
+permissions:
+  contents: read
+jobs:
+  push:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
+      - uses: ariga/setup-atlas@v0
+        with:
+          cloud-token: ${{ secrets.ATLAS_TOKEN }}
+      - uses: ariga/atlas-action/schema/push@master
+        with:
+          env: dev # Use the "dev" environment from the atlas.hcl file.
+```
+
+:::info ATLAS_TOKEN
+To push the schema to Atlas Registry from your GitHub Action, set up a GitHub secret named `ATLAS_TOKEN` using your Atlas
+Cloud token. To create a token, follow [these instructions](/cloud/bots).
+:::
+
+#### Set Up the `schema/plan` GitHub Action
+
+The last step is to set up the `schema/plan` and `schema/plan/approve` Actions:
+- `schema/plan` - This action automatically plans the schema migration whenever changes are made to the SQL schema file.
+  After a plan is created, it is pushed to the Atlas Registry in `PENDING` state, and waiting to be approved.
+- `schema/plan/approve` - This action automatically approves the pending plan in the Atlas Registry after the PR is
+  merged to the master branch.
+
+```yaml title=".github/workflows/schema-plan.yml"
+name: Plan Declarative Migrations
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - master
+    paths:
+      - .github/workflows/atlas-plan.yaml
+      - 'schema.sql'
+  pull_request:
+    branches:
+      - master
+    paths:
+      - .github/workflows/atlas-plan.yaml
+      - 'schema.sql'
+permissions:
+  contents: read
+  pull-requests: write
+jobs:
+  plan:
+    name: plan
+    if: ${{ github.event_name == 'pull_request' }}
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Atlas
+        uses: ariga/setup-atlas@master
+        with:
+          cloud-token: ${{ secrets.ATLAS_TOKEN }}
+      - name: Run schema plan
+        uses: ariga/atlas-action/schema/plan@master
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+        with:
+          env: dev # Use the "dev" environment from the atlas.hcl file.
+  approve:
+    name: approve
+    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+    runs-on: ubuntu-latest
+    env:
+      GITHUB_TOKEN: ${{ github.token }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Atlas
+        uses: ariga/setup-atlas@v0
+        with:
+          cloud-token: ${{ secrets.ATLAS_TOKEN }}
+      - name: Approve the plan
+        id: plan-approve
+        uses: ariga/atlas-action/schema/plan/approve@master
+        with:
+          env: dev # Use the "dev" environment from the atlas.hcl file.
+```
+
+:::info Plan Inputs
+- `from` - Defines the current state of the schema to calculate the migration from. If not provided, Atlas will use the
+  `url` in the `atlas.hcl` file (same as `atlas schema apply`). If the `url` attribute is not set, Atlas will use the
+  last known state from the Atlas Registry.
+- `to` - Defines the desired state of the schema to calculate the migration to. If not provided, Atlas will use the
+  `schema.src` attribute in the `atlas.hcl` file.
+:::
+
+To avoid a race condition between the `push` and `plan` workflows, we can merge them into a single workflow.
+
+```yaml title=".github/workflows/atlas-schema.yml"
+name: Plan Declarative Migrations
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - master
+    paths:
+      - .github/workflows/atlas-schema.yaml
+      - 'schema.sql'
+  pull_request:
+    branches:
+      - master
+    paths:
+      - .github/workflows/atlas-schema.yaml
+      - 'schema.sql'
+permissions:
+  contents: read
+  pull-requests: write
+jobs:
+  plan:
+    name: plan
+    if: ${{ github.event_name == 'pull_request' }}
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Atlas
+        uses: ariga/setup-atlas@master
+        with:
+          cloud-token: ${{ secrets.ATLAS_TOKEN }}
+      - name: Run schema plan
+        uses: ariga/atlas-action/schema/plan@master
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+        with:
+          env: dev
+  approve-push:
+    name: approve-push
+    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
+    runs-on: ubuntu-latest
+    env:
+      GITHUB_TOKEN: ${{ github.token }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Atlas
+        uses: ariga/setup-atlas@v0
+        with:
+          cloud-token: ${{ secrets.ATLAS_TOKEN }}
+      # Plan against the latest schema state (one before the PR).
+      - name: Approve the plan
+        id: plan-approve
+        uses: ariga/atlas-action/schema/plan/approve@master
+        with:
+          env: dev
+      # Push the schema after the plan is approved.
+      - name: Push the schema
+        id: schema-push
+        uses: ariga/atlas-action/schema/push@master
+        with:
+          env: dev
+```
+
diff --git a/doc/md/hcldoc/config.mdx b/doc/md/hcldoc/config.mdx
index 67e79de7..fff2917b 100644
--- a/doc/md/hcldoc/config.mdx
+++ b/doc/md/hcldoc/config.mdx
@@ -2600,6 +2600,15 @@ migration {
       <td id="env.migration.dir"><a href="#env.migration.dir"><code>dir</code></a></td>
     <td><code>false</code></td>
   <td><code>string</code></td>
+  </tr>
+  <tr>
+      <td id="env.migration.exclude"><a href="#env.migration.exclude"><code>exclude</code></a></td>
+    <td><code>false</code></td>
+  <td class="no-margin-bottom">
+
+List of strings
+  </td>
+
   </tr>
   <tr>
       <td id="env.migration.exec_order"><a href="#env.migration.exec_order"><code>exec_order</code></a></td>
diff --git a/doc/md/monitoring/01-home.mdx b/doc/md/monitoring/01-home.mdx
index 6c82478c..3fe421cc 100644
--- a/doc/md/monitoring/01-home.mdx
+++ b/doc/md/monitoring/01-home.mdx
@@ -73,6 +73,12 @@ Monitoring provides:
         icon="kubernetes-icon.svg"
         url="/monitoring/helm"
     />
+    <GuideCard
+        name="Terraform (AWS)"
+        description="Deploy the Atlas Agent to AWS using Terraform."
+        icon="terraform-icon.svg"
+        url="/monitoring/terraform"
+    />
 </div>
 
 </Layout>
\ No newline at end of file
diff --git a/doc/md/monitoring/terraform.mdx b/doc/md/monitoring/terraform.mdx
new file mode 100644
index 00000000..0c58e1d9
--- /dev/null
+++ b/doc/md/monitoring/terraform.mdx
@@ -0,0 +1,421 @@
+---
+title: Deploying with Terraform to AWS
+---
+
+## Intro
+
+This guide will walk you through deploying the Atlas Agent to your AWS account using Terraform.
+The Atlas Agent is a lightweight process that connects to your database and reports back schema metadata to Atlas Cloud.
+
+In this guide we will deploy a serverless Fargate task that runs the Atlas Agent in the same VPC as your database.
+
+### Prerequisites
+
+* A database running in AWS RDS
+* An Atlas Cloud Bot Token (follow the [Quickstart](/monitoring/quickstart) guide to get one)
+* Terraform installed on your local machine
+
+### Architecture
+
+Our solution is based on deploying the following pieces:
+* Use **AWS Secrets Manager** to store the Atlas Agent API Key and the database password. ECS cluster in your AWS account.
+* We will deploy a **Fargate** task that runs the Atlas Agent in the same VPC as your database.
+  To enable this, we will also create the needed serverless ECS cluster, security groups and necessary IAM roles.
+
+## Step 1: Preparing to deploy
+
+### Step 1a: Create a secret for the Atlas Agent Token
+
+Create a secret in AWS Secrets Manager to store the Atlas Agent Token.
+
+```
+aws secretsmanager create-secret --name atlas_agent_token --secret-string "aci_<redacted>"
+```
+
+### Step 1b: Create a secret for the database password
+
+Next, create a secret to store the database password:
+
+```
+aws secretsmanager create-secret --name atlas_agent_db_password --secret-string "<redacted>"
+```
+
+### Step 1c: Locate the target subnet
+
+Before building the Terraform configuration, you need to locate the subnet to which you would like to
+deploy the Fargate task. A few considerations to keep in mind:
+* The subnet should be in the same VPC as your database.
+* The subnet should have outbound internet access to reach the Atlas Cloud API as well as download the Atlas Agent image
+  from Docker Hub.
+
+## Step 2: Building the Terraform configuration
+
+### Step 2a: Import the AWS provider
+
+Create a new directory and create a `main.tf`, this file imports the AWS provider and configures it:
+
+```hcl title="main.tf"
+terraform {
+  required_providers {
+    aws = {
+      source  = "hashicorp/aws"
+      version = "5.66.0"
+    }
+  }
+}
+
+provider "aws" {
+  region  = "us-east-1" # Replace with the region where your database is running
+}
+
+data "aws_caller_identity" "current" {}
+
+data "aws_region" "current" {}
+```
+
+### Step 2b: Define the input variables
+
+Next, create a new file named `variables.tf` with the following content:
+
+```hcl title="variables.tf"
+variable "db_pass_secret" {
+  type = string
+}
+
+variable "atlas_cloud_api_secret" {
+  type = string
+}
+
+variable "name" {
+  type    = string
+  default = "atlas-agent"
+}
+
+variable "agent_subnet_id" {
+  type = string
+}
+```
+
+We defined 3 variables:
+* `name` - The name of the ECS task and other resources.
+* `db_pass_secret` - The name of the secret in AWS Secrets Manager that stores the database password.
+* `atlas_cloud_api_secret` - The name of the secret in AWS Secrets Manager that stores the Atlas Cloud API token.
+
+### Step 2c: Define the IAM roles
+
+Now, create a new file named `roles.tf` with the following content:
+
+```hcl title="roles.tf"
+# Fetch metadata about the Atlas Cloud token from Secrets Manager
+data "aws_secretsmanager_secret" "atlas_cloud_token" {
+  name = var.atlas_cloud_api_secret
+}
+
+# Fetch metadata about the database password from Secrets Manager
+data "aws_secretsmanager_secret" "db_password" {
+  name = var.db_pass_secret
+}
+
+# Create an IAM role for the ECS task execution
+resource "aws_iam_role" "ecs_task_execution_role" {
+  name = "${var.name}-ecs_task_execution_role"
+
+  assume_role_policy = jsonencode({
+    Version = "2012-10-17",
+    Statement = [
+      {
+        Effect = "Allow",
+        Principal = {
+          Service = "ecs-tasks.amazonaws.com"
+        },
+        Action = "sts:AssumeRole"
+      }
+    ]
+  })
+}
+
+# Attach the Amazon ECS task execution policy to the role
+resource "aws_iam_role_policy_attachment" "ecs_task_execution_policy" {
+  role       = aws_iam_role.ecs_task_execution_role.name
+  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
+}
+
+# Grant access to Secrets Manager for both the database password and the Atlas Cloud token
+resource "aws_iam_policy" "access_secrets" {
+  name = "${var.name}-access-secrets"
+  policy = jsonencode({
+    Version = "2012-10-17",
+    Statement = [
+      {
+        Effect = "Allow",
+        Action = [
+          "secretsmanager:GetSecretValue"
+        ],
+        Resource = [
+          data.aws_secretsmanager_secret.db_password.arn,
+          data.aws_secretsmanager_secret.atlas_cloud_token.arn
+        ]
+      }
+    ]
+  })
+}
+
+resource "aws_iam_role_policy_attachment" "ecs_task_execution_policy_attachment" {
+  policy_arn = aws_iam_policy.access_secrets.arn
+  role       = aws_iam_role.ecs_task_execution_role.name
+}
+```
+
+Here's what this file does:
+* Defines an IAM role for the ECS task execution. This role is used by the ECS cluster to run the Fargate task.
+* Creates a policy that grants access to the Secrets Manager secrets that store the database password and the Atlas Cloud API token.
+* Attaches the policy to the ECS task execution role.
+
+
+### Step 2d: Enable CloudWatch logging
+
+When running any workload on ECS, it's a good practice to enable logging to CloudWatch. This helps us gain visibility into
+the task's behavior and troubleshoot any issues that may arise.
+
+Let's create a file named `logs.tf` with the following content:
+
+```hcl title="logs.tf"
+
+# Policy to allow ECS task to write logs to CloudWatch.
+resource "aws_iam_policy" "ecs_cloudwatch_logs_policy" {
+  name = "${var.name}-ecs-cloudwatch-logs-policy"
+  policy = jsonencode({
+    Version = "2012-10-17",
+    Statement = [
+      {
+        Effect = "Allow",
+        Action = [
+          "logs:CreateLogStream",
+          "logs:PutLogEvents"
+        ],
+        Resource = "arn:aws:logs:us-east-1:${data.aws_caller_identity.current.account_id}:log-group:/ecs/atlas-agent:*"
+      }
+    ]
+  })
+}
+
+# Attach the policy to the ECS task execution role
+resource "aws_iam_role_policy_attachment" "ecs_task_execution_cloudwatch_logs_policy" {
+  role       = aws_iam_role.ecs_task_execution_role.name
+  policy_arn = aws_iam_policy.ecs_cloudwatch_logs_policy.arn
+}
+
+# Create a CloudWatch log group for the ECS task
+resource "aws_cloudwatch_log_group" "ecs_log_group" {
+  name              = "/ecs/atlas-agent-${var.name}"
+  retention_in_days = 7 # Adjust retention as needed
+}
+```
+
+This file creates a CloudWatch log group for the ECS task and attaches the necessary policy to the ECS task execution role.
+
+### Step 2e: Define the ECS cluster and task
+
+Next, create a file named `faragte.tf` with the following content:
+
+```hcl title="fargate.tf"
+# Define the ECS cluster
+resource "aws_ecs_cluster" "atlas_cluster" {
+  name = var.name
+}
+
+# Define the ECS service
+resource "aws_ecs_service" "atlas_service" {
+  name            = "atlas-service"
+  cluster         = aws_ecs_cluster.atlas_cluster.id
+  task_definition = aws_ecs_task_definition.atlas_task.arn
+  desired_count   = 1
+  launch_type     = "FARGATE"
+
+  network_configuration {
+    subnets = [var.agent_subnet_id]
+    security_groups = [aws_security_group.ecs.id]
+    assign_public_ip = true
+  }
+}
+
+# Define the Fargate task definition.
+resource "aws_ecs_task_definition" "atlas_task" {
+  family             = "atlas-task"
+  network_mode       = "awsvpc"
+  requires_compatibilities = ["FARGATE"]
+  cpu                = "256"
+  memory             = "512"
+  execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
+
+  container_definitions = jsonencode([
+    {
+      name  = "atlas-agent"
+      image = "arigaio/atlas-agent:latest"
+      secrets = [
+        {
+          name      = "ATLAS_CLOUD_TOKEN"
+          valueFrom = data.aws_secretsmanager_secret.atlas_cloud_token.arn
+        },
+        {
+          name      = "RDS_DB_PASS"
+          valueFrom = data.aws_secretsmanager_secret.db_password.arn
+        }
+      ]
+      portMappings = [
+        {
+          containerPort = 80
+          protocol      = "tcp"
+        }
+      ]
+      logConfiguration = {
+        logDriver = "awslogs"
+        options = {
+          awslogs-group         = aws_cloudwatch_log_group.ecs_log_group.name
+          awslogs-region        = data.aws_region.current.name
+          awslogs-stream-prefix = "atlas-agent"
+        }
+      }
+      healthCheck = {
+        command = ["CMD-SHELL", "curl -f http://localhost/readyz || exit 1"]
+        interval    = 30
+        timeout     = 5
+        retries     = 3
+        startPeriod = 60
+      }
+    }
+  ])
+}
+```
+
+This file defines the ECS cluster, service, and task. Here are some important points about the Faragte task definition:
+* The task uses the `arigaio/atlas-agent:latest` image.
+* The task definition includes the Atlas Cloud API token and the database password as secrets. These secrets are fetched
+  from AWS Secrets Manager using the IAM role we defined earlier.
+* The task logs are sent to CloudWatch logs.
+* The task has a health check that ensures the agent is running correctly.
+
+### Step 2f: Define the security group
+
+The final piece we need to define is the security group that allows the Fargate task to communicate with the database.
+Create a file named `security_group.tf` with the following content:
+
+```hcl title="security_group.tf"
+data "aws_subnet" "subnet" {
+  id = var.agent_subnet_id
+}
+
+# Define a security group for the ECS service
+resource "aws_security_group" "ecs" {
+  name        = "${var.name}-ecs-sg"
+  description = "Allows egress from atlas-agent"
+  vpc_id      = data.aws_subnet.subnet.vpc_id
+
+  # Allow all outbound traffic
+  egress {
+    from_port = 0
+    to_port   = 0
+    protocol = "-1" # -1 means all protocols
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  tags = {
+    Name = "${var.name}-sg"
+  }
+}
+
+# Define a sg that accepts ingress from the agent sg
+resource "aws_security_group" "agent" {
+  name        = "${var.name}-agent-sg"
+  description = "Allows ingress from atlas-agent"
+  vpc_id      = data.aws_subnet.subnet.vpc_id
+
+  ingress {
+    from_port   = 0
+    to_port     = 0
+    protocol    = "-1"
+    security_groups = [aws_security_group.ecs.id]
+  }
+
+  tags = {
+    Name = "${var.name}-agent-sg"
+  }
+}
+
+output "agent_sg_id" {
+  value = aws_security_group.agent.id
+}
+```
+This file creates two security groups:
+* The first security group allows all outbound traffic from the Fargate task. This is needed to allow the agent to
+  communicate with the Atlas Cloud API.
+* The second security group allows inbound traffic from the first security group. This is needed to allow connections
+  from the Fargate task to the database.
+
+Additionally, the file defines an output variable that returns the ID of the security group that allows inbound traffic
+from the Fargate task.
+
+## Step 3: Deploying the configuration
+
+### Step 3a: Initialize Terraform
+
+Run the following command to initialize Terraform:
+
+```shell
+terraform init
+```
+
+### Step 3b: Create a `.tfvars` file
+
+Create a file named `terraform.tfvars` with the following content:
+
+```hcl
+atlas_cloud_api_secret = "atlas_agent_token"        # The name of the secret from step 1a
+db_pass_secret         = "atlas_agent_db_password"  # The name of the secret from step 1b
+agent_subnet_id        = "subnet-0c12e1fd02df0ea0a" # The subnet id from step 1c
+```
+
+### Step 3c: Apply the configuration
+
+Run the following command to see what Terraform will do:
+
+```shell
+terraform apply
+```
+
+Terraform will show you a plan of the changes it will make. If everything looks good, type `yes` to apply the changes.
+
+Terraform will create the necessary resources in your AWS account. Once the process is complete, you will see the output
+variables defined in the configuration:
+
+```shell
+Outputs:
+
+agent_sg_id = "sg-0c12e1fd02df0ea0a"
+```
+
+Make a note of the `agent_sg_id` value, as you will need it to configure the database security group.
+
+### Step 3d: Configure the database security group
+
+The final step is to configure the security group of your database to allow inbound traffic from the security group
+associated with the Fargate task. This allows the agent to connect to the database.
+
+This may be done in various ways depending on how you configured your database.
+
+* If your database is running in RDS, you can modify the security group associated with the RDS instance to allow inbound
+  traffic from the security group associated with the Fargate task.
+* If your database is running in EC2, you can modify the security group associated with the EC2 instance to allow inbound
+  traffic from the security group associated with the Fargate task.
+
+## Step 4: Verify the deployment
+
+Once you have configured the database security group, the agent task should start running in your AWS account.
+
+To verify that the agent is able to connect to Atlas Cloud:
+
+1. Go to the "Monitoring" section in your Atlas Cloud account.
+2. Click on the "Agents" tab on the top navigation bar.
+3. You should see the agent you just deployed listed there. See example below.
+
+![](https://atlasgo.io/uploads/cloud/connected-agent.png)
\ No newline at end of file
diff --git a/doc/md/reference.md b/doc/md/reference.md
index ee41f85a..b8e12f77 100644
--- a/doc/md/reference.md
+++ b/doc/md/reference.md
@@ -383,13 +383,18 @@ atlas migrate new [flags] [name]
 
 ### atlas migrate push
 
-Push a migration directory with an optional tag to the Atlas registry
+Push a migration directory with an optional tag to the Atlas Registry
 
 #### Usage
 ```
 atlas migrate push [flags] directory[:tag]
 ```
 
+#### Details
+The 'atlas migrate push' command allows you to push your migration directory to the Atlas Registry.
+If no repository exists in the registry for the pushed directory, a new one is created. Otherwise, the
+directory state will be updated. Read more: https://atlasgo.io/registry
+
 #### Example
 
 ```
@@ -636,6 +641,8 @@ migration.
       --auto-approve      apply changes without prompting for approval
       --format string     Go template to use to format the output
       --tx-mode string    set transaction mode [none, file] (default "file")
+      --plan string       URL to a pre-planned migration (e.g., atlas://repo/plans/name)
+      --edit              open the generated SQL in an editor
 
 ```
 
@@ -661,8 +668,10 @@ As a safety feature, 'atlas schema clean' will ask for confirmation before attem
 ```
 #### Flags
 ```
-  -u, --url string     [driver://username:password@address/dbname?param=value] select a resource using the URL format
-      --auto-approve   apply changes without prompting for approval
+  -u, --url string      [driver://username:password@address/dbname?param=value] select a resource using the URL format
+      --dry-run         print SQL without executing it
+      --format string   Go template to use to format the output
+      --auto-approve    apply changes without prompting for approval
 
 ```
 
@@ -765,6 +774,256 @@ flag.
 ```
 
 
+### atlas schema plan
+
+Plan a declarative migration for the schema transition
+
+#### Usage
+```
+atlas schema plan [flags] [name]
+```
+
+#### Details
+The 'atlas schema plan' command allows users to pre-plan, review, and approve migrations before
+executing 'atlas schema apply' on the database. This enables users to preview and modify SQL changes,
+involve team members in the review process, and ensure that no human intervention is required during
+the atlas schema apply phase. Read more: https://atlasgo.io/declarative/plan
+
+#### Example
+
+```
+  atlas schema plan --env dev
+  atlas schema plan --env dev --to file://schema.hcl
+  atlas schema plan --env dev --from file://schema.hcl
+  atlas schema plan --env dev --pending
+```
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+      --dry-run                 print the plan and exit
+      --push                    push the plan to the registry
+      --save                    save the plan to a file
+      --edit                    edit the plan in the terminal editor
+      --name string             plan name as stored in the registry
+      --pending                 push the plan in a pending state
+  -o, --output string           output file path (e.g., path/to/file.plan.hcl)
+
+```
+
+
+#### atlas schema plan approve
+
+Approve a migration plan by its registry URL
+
+#### Usage
+```
+atlas schema plan approve [flags]
+```
+
+#### Example
+
+```
+  atlas schema plan approve --url atlas://<schema-slug>/plans/<name>
+```
+#### Flags
+```
+      --url string      URL to the plan file in the registry
+      --format string   output format
+
+```
+
+
+#### atlas schema plan lint
+
+Run analysis (migration linting) on a plan file
+
+#### Usage
+```
+atlas schema plan lint [flags]
+```
+
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+  -f, --file string             URL to the plan file (e.g., file://path/to/file.plan.hcl)
+
+```
+
+
+#### atlas schema plan list
+
+List all plans in the registry for the given schema transition
+
+#### Usage
+```
+atlas schema plan list [flags]
+```
+
+#### Example
+
+```
+  atlas schema plan list --to file://schema.hcl --env dev
+```
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+      --pending                 list only pending plans
+
+```
+
+
+#### atlas schema plan new
+
+Create a new plan file for the schema transition
+
+#### Usage
+```
+atlas schema plan new [flags]
+```
+
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+      --edit                    edit the plan in the terminal editor
+      --name string             plan name as stored in the registry
+  -o, --output string           output file path (e.g., path/to/file.plan.hcl)
+
+```
+
+
+#### atlas schema plan pull
+
+Pull a plan file from the registry
+
+#### Usage
+```
+atlas schema plan pull [flags]
+```
+
+#### Flags
+```
+      --url string   URL to the plan file in the registry
+
+```
+
+
+#### atlas schema plan push
+
+Push a plan file to the registry
+
+#### Usage
+```
+atlas schema plan push [flags]
+```
+
+#### Example
+
+```
+  atlas schema plan push --file file://plan.hcl
+  atlas schema plan push --from file://schema.hcl --to file://schema.hcl --file file://plan.hcl
+```
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+  -f, --file string             URL to the plan file (e.g., file://path/to/file.plan.hcl)
+      --pending                 push the plan in a pending state
+
+```
+
+
+#### atlas schema plan validate
+
+Validate a plan file against the schema transition
+
+#### Usage
+```
+atlas schema plan validate [flags]
+```
+
+#### Example
+
+```
+  atlas schema plan validate --file file://plan.hcl
+  atlas schema plan validate --from file://schema.hcl --to file://schema.hcl --file file://plan.hcl
+```
+#### Flags
+```
+      --from strings            URL(s) of the current schema state
+      --to strings              URL(s) of the desired schema state
+      --auto-approve            approve the plan without asking for confirmation
+      --repo string             URL to the schema repository
+      --format string           output format
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+  -f, --file string             URL to the plan file (e.g., file://path/to/file.plan.hcl)
+
+```
+
+
+### atlas schema push
+
+Push the schema with an optional tag to the Atlas Registry
+
+#### Usage
+```
+atlas schema push [flags] schema[:tag]
+```
+
+#### Details
+The 'atlas schema push' command allows you to push your schema definition to the Atlas Registry.
+If no repository exists in the registry for the schema, a new one is created. Otherwise, a new
+version is generated. Read more: https://atlasgo.io/registry
+
+#### Example
+
+```
+  atlas schema push --dev-url docker://mysql/8/dev --url file://schema.hcl app
+  atlas schema push --env dev app
+  atlas schema push --env dev app --version 20230102150405
+```
+#### Flags
+```
+  -u, --url strings             desired schema URL(s) to push
+      --dev-url string          [driver://username:password@address/dbname?param=value] select a dev database using the URL format
+      --version string          version of the schema to push. Defaults to the current timestamp
+      --desc string             description of the pushed schema version
+      --tag string              tag to push the directory with
+      --format string           Go template to use to format the output
+      --lock-timeout duration   set how long to wait for the database lock (default 10s)
+
+```
+
+
 ### atlas schema test
 
 Run schema tests against the desired schema
diff --git a/doc/md/versioned/import.mdx b/doc/md/versioned/import.mdx
index 5a1a96fa..764a6a4e 100644
--- a/doc/md/versioned/import.mdx
+++ b/doc/md/versioned/import.mdx
@@ -8,7 +8,7 @@ import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
 Atlas supports the generation of [custom migration file formats](/versioned/diff#generate-migrations-with-custom-formats)
-for a variety of existing migration management tools, e.g. [Flyway](https://flywaydb.org/) or
+for a variety of existing migration management tools, e.g. [Flyway](https://atlasgo.io/atlas-vs-others) or
 [golang-migrate/migrate](https://github.com/golang-migrate/migrate). But Atlas has its own format as well and provides
 a convenient command to import existing migration directories of supported tools into the Atlas format.
 
diff --git a/doc/website/blog/2022-08-11-announcing-versioned-migration-authoring.mdx b/doc/website/blog/2022-08-11-announcing-versioned-migration-authoring.mdx
index 559520a7..d1b3374c 100644
--- a/doc/website/blog/2022-08-11-announcing-versioned-migration-authoring.mdx
+++ b/doc/website/blog/2022-08-11-announcing-versioned-migration-authoring.mdx
@@ -22,7 +22,7 @@ schemas that we have been experimenting with in the past months: _Versioned Migr
 * Many teams prefer a more imperative approach where each change to the database schema 
  is checked-in to source control and reviewed during code-review. This type of workflow
  is commonly called _versioned migrations_ (or _change based migrations_) and is supported by many established tools such
- as [Flyway](https://flyway.org) and [Liquibase](https://liquibase.org).
+ as [Flyway](https://atlasgo.io/atlas-vs-others) and [Liquibase](https://atlasgo.io/atlas-vs-others).
 * The downside of the versioned migration approach is, of course, that it puts the burden of 
  planning the migration on developers. As part of the Atlas project we advocate for 
 a third combined approach that we call "Versioned Migration Authoring". 
@@ -104,7 +104,7 @@ prefer a more imperative approach where each change to the database schema is ch
 to source control and reviewed during code-review. Each such change
 is called a "migration", as it migrates the database schema from the previous version to
 the next. To support this kind of requirement, many popular database schema management
-tools such as [Flyway](https://flywaydb.org/), [Liquibase](https://liquibase.org/) or
+tools such as [Flyway](https://atlasgo.io/atlas-vs-others), [Liquibase](https://atlasgo.io/atlas-vs-others) or
 [golang-migrate](https://github.com/golang-migrate/migrate) support a workflow that
 is commonly called "versioned migrations".
 
diff --git a/doc/website/blog/2023-05-31-atlas-v012.mdx b/doc/website/blog/2023-05-31-atlas-v012.mdx
index 3e23992a..8a4e3fcc 100644
--- a/doc/website/blog/2023-05-31-atlas-v012.mdx
+++ b/doc/website/blog/2023-05-31-atlas-v012.mdx
@@ -13,7 +13,7 @@ the next batch of features that we are releasing to open-source and to Atlas Clo
 In summary, version v0.12 includes a few major features that are explained in detail below:
 
 1. We have added support for importing and running migration linting on GitHub PRs for external migration
-   formats, such as [Flyway](https://flywaydb.org/) and [golang-migrate](https://github.com/golang-migrate/migrate).
+   formats, such as [Flyway](https://atlasgo.io/atlas-vs-others) and [golang-migrate](https://github.com/golang-migrate/migrate).
 2. Atlas now supports reading your migration directory directly from your Atlas Cloud account.
    This eliminates the need for users to build their Docker images with the directory content and
    makes running schema migrations in production much easier.
diff --git a/doc/website/blog/2023-12-11-gitops-for-databases-part-2.mdx b/doc/website/blog/2023-12-11-gitops-for-databases-part-2.mdx
index 868c067f..85a2cae4 100644
--- a/doc/website/blog/2023-12-11-gitops-for-databases-part-2.mdx
+++ b/doc/website/blog/2023-12-11-gitops-for-databases-part-2.mdx
@@ -31,7 +31,7 @@ changes has evolved hand in hand with modern devops principles into what is know
 [database migrations](https://en.wikipedia.org/wiki/Schema_migration). As part of this evolution, hundreds of
 "migration tools" have been created to help developers manage their database migrations. These tools range from
 ORM and language specific tools like [Alembic](https://alembic.sqlalchemy.org/en/latest/) for Python, to language
-agnostic tools like [Flyway](https://flywaydb.org/) and [Liquibase](https://www.liquibase.org/).
+agnostic tools like [Flyway](https://atlasgo.io/atlas-vs-others) and [Liquibase](https://atlasgo.io/atlas-vs-others).
 
 When Kubernetes came along and teams started to containerize their applications, the knee-jerk reaction was to
 wrap these legacy tools in a container and run them as part of the application deployment process. We
diff --git a/doc/website/docusaurus.config.js b/doc/website/docusaurus.config.js
index a0186b96..362c1433 100644
--- a/doc/website/docusaurus.config.js
+++ b/doc/website/docusaurus.config.js
@@ -217,6 +217,10 @@ module.exports = {
             to: "/guides/deploying/remote-directories",
             from: "/guides/deploying/from-cloud",
           },
+          {
+            from: '/registry',
+            to: '/cloud/features/registry',
+          },
         ],
       },
     ],
diff --git a/doc/website/package-lock.json b/doc/website/package-lock.json
index 294ec411..f49325df 100644
--- a/doc/website/package-lock.json
+++ b/doc/website/package-lock.json
@@ -8,7 +8,7 @@
       "name": "website",
       "version": "0.0.0",
       "dependencies": {
-        "@ariga/atlas-website": "^0.0.63",
+        "@ariga/atlas-website": "^0.0.65",
         "@docusaurus/core": "^2.0.0-rc.1",
         "@docusaurus/plugin-client-redirects": "^2.4.1",
         "@docusaurus/plugin-ideal-image": "^2.4.1",
@@ -435,9 +435,9 @@
       }
     },
     "node_modules/@ariga/atlas-website": {
-      "version": "0.0.63",
-      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.63.tgz",
-      "integrity": "sha512-/w3KvlCOk4un6ceYKUbORQKHR3+gBcIWIWVgzCYifOKEIQ/rFObj0PtFG7N9cjzEMvsIes3kQw+DGwrk62v4Jw==",
+      "version": "0.0.65",
+      "resolved": "https://registry.npmjs.org/@ariga/atlas-website/-/atlas-website-0.0.65.tgz",
+      "integrity": "sha512-bFzKGrnnzlQ8UyI0XiwRBZTXibEub39ky9ovXFlAu+WfZnIhNe6ck8Mk93it5vgzmpz4V27BmSGObrW2JcwSOA==",
       "dependencies": {
         "@amplitude/analytics-browser": "^1.9.4",
         "@types/amplitude-js": "^8.16.2",
@@ -463,6 +463,7 @@
         "react-tooltip": "^5.26.0",
         "react-use-intercom": "^5.4.0",
         "replace-in-file": "^6.3.5",
+        "rollup-plugin-visualizer": "^5.12.0",
         "tailwind-merge": "^2.2.1",
         "tailwind-scrollbar": "^2.1.0",
         "tailwindcss": "^3.2.6",
@@ -22577,6 +22578,74 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/rollup-plugin-visualizer": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz",
+      "integrity": "sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==",
+      "dependencies": {
+        "open": "^8.4.0",
+        "picomatch": "^2.3.1",
+        "source-map": "^0.7.4",
+        "yargs": "^17.5.1"
+      },
+      "bin": {
+        "rollup-plugin-visualizer": "dist/bin/cli.js"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "rollup": "2.x || 3.x || 4.x"
+      },
+      "peerDependenciesMeta": {
+        "rollup": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/rollup-plugin-visualizer/node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/rst-selector-parser": {
       "version": "2.2.3",
       "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
diff --git a/doc/website/package.json b/doc/website/package.json
index f52aa52d..49e4d581 100644
--- a/doc/website/package.json
+++ b/doc/website/package.json
@@ -16,7 +16,7 @@
     "invalidate-cdn": "aws cloudfront create-invalidation --distribution-id E3VOJYSV9YO33D --paths \"/*\""
   },
   "dependencies": {
-    "@ariga/atlas-website": "^0.0.63",
+    "@ariga/atlas-website": "^0.0.65",
     "@docusaurus/core": "^2.0.0-rc.1",
     "@docusaurus/plugin-client-redirects": "^2.4.1",
     "@docusaurus/plugin-ideal-image": "^2.4.1",
diff --git a/doc/website/sidebars.js b/doc/website/sidebars.js
index 0308f94b..72904b6c 100644
--- a/doc/website/sidebars.js
+++ b/doc/website/sidebars.js
@@ -36,6 +36,7 @@ module.exports = {
             items: [
                 {type: 'doc', id: 'declarative/inspect', label: 'Schema Inspection'},
                 {type: 'doc', id: 'declarative/apply', label: 'Applying Changes'},
+                {type: 'doc', id: 'declarative/plan', label: 'Pre-planning Changes'},
                 {type: 'doc', id: 'declarative/diff', label: 'Comparing Schemas'},
             ]
         },
@@ -149,6 +150,10 @@ module.exports = {
                         {
                             type: 'doc',
                             id: 'monitoring/helm'
+                        },
+                        {
+                            type: 'doc',
+                            id: 'monitoring/terraform'
                         }
                     ]
                 },
diff --git a/doc/website/src/css/custom.css b/doc/website/src/css/custom.css
index c84656b7..4802201e 100644
--- a/doc/website/src/css/custom.css
+++ b/doc/website/src/css/custom.css
@@ -311,6 +311,10 @@ h3 > .login {
   vertical-align: middle;
 }
 
+/*
+Migrate apply log.
+*/
+
 .language-applylog .version, .language-applylog .action2, .language-applylog .action3 {
   color: #62cece;
 }
@@ -324,44 +328,72 @@ h3 > .login {
   color: white;
 }
 
+/*
+Schema plan log.
+*/
+
+.language-planlog .action2, .language-planlog .action3, .language-planlog .state, .language-planlog .atlaslink {
+  color: #62cece;
+}
+
+.language-planlog .action1 {
+  color: #c0c028;
+}
+
+.language-planlog .questionmark {
+  color: #417fb9;
+  font-weight: bold;
+}
+
+.language-planlog .approved {
+  color: #8bc34a;
+}
+
 .language-testoutput .pass {
-  color: #c4e88d;
+color: #c4e88d;
 }
 
 .language-testoutput .fail {
-  color: #db4b4b;
+color: #db4b4b;
+}
+
+/*
+Improve YAML
+ */
+.language-yaml span.token.key.atrule {
+  color: #9CDCFE; /* Same color as HCL properties */
 }
 
 .sticky-anchor {
-  scroll-margin-top:calc(var(--ifm-navbar-height) + 0.5rem);
+scroll-margin-top:calc(var(--ifm-navbar-height) + 0.5rem);
 }
 
 
 .docs-layout h3 {
-  @apply text-xl font-bold mb-4 flex;
+@apply text-xl font-bold mb-4 flex;
 }
 
 .docs-layout h1 {
-  @apply text-4xl;
+@apply text-4xl;
 }
 
 .docs-layout .tabs__item {
-  @apply px-2 py-2.5 text-center flex items-center; 
+@apply px-2 py-2.5 text-center flex items-center;
 }
 
 .docs-layout .tabs {
-  @apply border-b;
+@apply border-b;
 }
 
 .docs-layout p code {
-  @apply rounded-lg border-none p-1 font-roboto;
+@apply rounded-lg border-none p-1 font-roboto;
 }
 
 div[class^='announcementBar'] {
-  @apply border-0;
+@apply border-0;
 }
 
 .footer__copyright {
-  font-size: 10px;
-  line-height: 14px;
+font-size: 10px;
+line-height: 14px;
 }
diff --git a/doc/website/src/theme/prism-include-languages.js b/doc/website/src/theme/prism-include-languages.js
index 2f458b2c..cebfe9d9 100644
--- a/doc/website/src/theme/prism-include-languages.js
+++ b/doc/website/src/theme/prism-include-languages.js
@@ -16,6 +16,16 @@ const prismIncludeLanguages = (Prism) => {
     'action3': /\s\s+->\s/,
     'error': /(Error:\s.+|\s+.+(assertions failed:|check assertion)\s.+)/i,
   };
+  Prism.languages.planlog = {
+    'action1': /\s\s+-{2}\s/,
+    'action2': /\s\s+-{25,}/,
+    'action3': /\s\s+->\s/,
+    'state': /(local database|file:\/\/schema\.sql)\s/,
+    'questionmark': /\? /,
+    'error': /(Error:\s.+|\s+.+(assertions failed:|check assertion)\s.+)/i,
+    'approved': /APPROVED/,
+    'atlaslink': /https:\/\/.+atlasgo.+|atlas:\/\/.+/
+  };
   Prism.languages.testoutput = {
     'dash2': /--/,
     'pass': / PASS/,
diff --git a/internal/ci/ci_dialect.tmpl b/internal/ci/ci_dialect.tmpl
index 606939c9..7341a427 100644
--- a/internal/ci/ci_dialect.tmpl
+++ b/internal/ci/ci_dialect.tmpl
@@ -14,7 +14,7 @@ env:
 jobs:
 {{- range $.Jobs }}
   integration-{{ .Version }}:
-    runs-on: {{ $.Runner }}
+    runs-on: {{ or .Runner $.Runner }}
     {{- if .Image }}
     services:
       {{ .Version }}:
diff --git a/internal/ci/main.go b/internal/ci/main.go
index 2b66544d..522750dd 100644
--- a/internal/ci/main.go
+++ b/internal/ci/main.go
@@ -32,6 +32,7 @@ type (
 	}
 	// Job defines an integration job to run.
 	Job struct {
+		Runner      string       // runner to use
 		Version     string       // version to test (passed to go test as flag which database dialect/version)
 		Image       string       // name of service
 		Credentials *Credentials // credentials to pull the image
diff --git a/sql/migrate/dir.go b/sql/migrate/dir.go
index 3690ab17..38b04540 100644
--- a/sql/migrate/dir.go
+++ b/sql/migrate/dir.go
@@ -513,9 +513,19 @@ func (d *MemDir) Path() string {
 	return d.path
 }
 
+const versionFormat = "20060102150405"
+
 // NewVersion generates a new migration version.
 func NewVersion() string {
-	return time.Now().UTC().Format("20060102150405")
+	return time.Now().UTC().Format(versionFormat)
+}
+
+// CheckVersion checks if the given version is valid Atlas version.
+func CheckVersion(v string) error {
+	if _, err := time.Parse(versionFormat, v); err != nil {
+		return fmt.Errorf("invalid version: %w. Expected format is: %s", err, versionFormat)
+	}
+	return nil
 }
 
 var (
diff --git a/sql/sqlite/migrate.go b/sql/sqlite/migrate.go
index 1badb9bf..36b4eba1 100644
--- a/sql/sqlite/migrate.go
+++ b/sql/sqlite/migrate.go
@@ -7,6 +7,7 @@ package sqlite
 import (
 	"context"
 	"fmt"
+	"slices"
 	"strings"
 
 	"ariga.io/atlas/sql/internal/sqlx"
@@ -557,7 +558,17 @@ func alterable(modify *schema.ModifyTable) bool {
 		switch change := change.(type) {
 		case *schema.RenameColumn, *schema.RenameIndex, *schema.DropIndex, *schema.AddIndex:
 		case *schema.AddColumn:
-			if len(change.C.Indexes) > 0 || len(change.C.ForeignKeys) > 0 || change.C.Default != nil {
+			if len(change.C.Indexes) > 0 || len(change.C.ForeignKeys) > 0 {
+				return false
+			}
+			// If the column has a DEFAULT clause, it must be a constant value.
+			// See: https://www.sqlite.org/lang_altertable.html#alter_table_add_column.
+			switch x := change.C.Default.(type) {
+			case *schema.Literal:
+				if slices.Contains([]string{"CURRENT_TIME", "CURRENT_DATE", "CURRENT_TIMESTAMP"}, x.V) {
+					return false
+				}
+			case *schema.RawExpr:
 				return false
 			}
 			// Only VIRTUAL generated columns can be added using ALTER TABLE.


Test patch:
diff --git a/cmd/atlas/internal/cmdapi/project_test.go b/cmd/atlas/internal/cmdapi/project_test.go
index ad65e8ce..6d8379dc 100644
--- a/cmd/atlas/internal/cmdapi/project_test.go
+++ b/cmd/atlas/internal/cmdapi/project_test.go
@@ -221,6 +221,15 @@ env "multi" {
 		sources, err := env.Sources()
 		require.NoError(t, err)
 		require.EqualValues(t, []string{"local/app.hcl"}, sources)
+
+		v, err := env.VarFromURL("env://dev")
+		require.NoError(t, err)
+		require.Equal(t, "docker://mysql/8", v)
+		_, err = env.VarFromURL("env://src")
+		require.EqualError(t, err, `env://src: expect one schema in env "local", got 2`)
+		v, err = env.VarFromURL("env://migration.dir")
+		require.NoError(t, err)
+		require.Equal(t, "file://migrations", v)
 	})
 	t.Run("multi", func(t *testing.T) {
 		_, envs, err := EnvByName(&cobra.Command{}, "multi", nil)
diff --git a/doc/md/hcldoc/testing.mdx b/doc/md/hcldoc/testing.mdx
index f436002e..60379a7e 100644
--- a/doc/md/hcldoc/testing.mdx
+++ b/doc/md/hcldoc/testing.mdx
@@ -204,6 +204,78 @@ test "migrate" "20240613061102" {
 
 
     
+#### `test.external` {#test.external}
+The `external` allows executing external programs to insert data or run assertions on the dev-database used by test case.
+
+~~~hcl
+external {
+  program = [
+    "go", "run", "seed.go",
+    "--dev-url", testing.dev_url,
+  ]
+}
+~~~
+
+
+
+
+##### `test.external` attributes {#test.external-attributes}
+
+
+
+<table class="attributes-table">
+  <tr>
+    <th>Name</th>
+    <th>Required</th>
+    <th>Value</th>
+  </tr>
+  <tr>
+      <td id="test.external.match"><a href="#test.external.match"><code>match</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+  <tr>
+      <td id="test.external.output"><a href="#test.external.output"><code>output</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+  <tr>
+      <td id="test.external.program"><a href="#test.external.program"><code>program</code></a></td>
+    <td><code>true</code></td>
+  <td class="no-margin-bottom">
+
+List of strings
+  </td>
+
+  </tr>
+  <tr>
+      <td id="test.external.working_dir"><a href="#test.external.working_dir"><code>working_dir</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+</table>
+
+
+
+
+##### `test.external` constraints {#test.external-constraints}
+
+
+
+|    Constraint               |           Value
+|-----------------------------|------------------------------------
+| Required                    |      `false`
+| Require Name                |      `false`
+| Repeatable                  |      `true`
+| Allow unknown blocks        |      `false`
+| Allow unknown attributes    |      `false`
+| Mutually exclusive sets     | `[output, match]`
+
+
+
+
+
+    
 #### `test.log` {#test.log}
 
 
@@ -494,6 +566,78 @@ test "schema" "postal" {
 
 
     
+#### `test.external` {#test.external}
+The `external` allows executing external programs to insert data or run assertions on the dev-database used by test case.
+
+~~~hcl
+external {
+  program = [
+    "go", "run", "seed.go",
+    "--dev-url", testing.dev_url,
+  ]
+}
+~~~
+
+
+
+
+##### `test.external` attributes {#test.external-attributes}
+
+
+
+<table class="attributes-table">
+  <tr>
+    <th>Name</th>
+    <th>Required</th>
+    <th>Value</th>
+  </tr>
+  <tr>
+      <td id="test.external.match"><a href="#test.external.match"><code>match</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+  <tr>
+      <td id="test.external.output"><a href="#test.external.output"><code>output</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+  <tr>
+      <td id="test.external.program"><a href="#test.external.program"><code>program</code></a></td>
+    <td><code>true</code></td>
+  <td class="no-margin-bottom">
+
+List of strings
+  </td>
+
+  </tr>
+  <tr>
+      <td id="test.external.working_dir"><a href="#test.external.working_dir"><code>working_dir</code></a></td>
+    <td><code>false</code></td>
+  <td><code>string</code></td>
+  </tr>
+</table>
+
+
+
+
+##### `test.external` constraints {#test.external-constraints}
+
+
+
+|    Constraint               |           Value
+|-----------------------------|------------------------------------
+| Required                    |      `false`
+| Require Name                |      `false`
+| Repeatable                  |      `true`
+| Allow unknown blocks        |      `false`
+| Allow unknown attributes    |      `false`
+| Mutually exclusive sets     | `[output, match]`
+
+
+
+
+
+    
 #### `test.log` {#test.log}
 
 
diff --git a/sql/migrate/dir_test.go b/sql/migrate/dir_test.go
index 3170fc14..d330dff2 100644
--- a/sql/migrate/dir_test.go
+++ b/sql/migrate/dir_test.go
@@ -575,6 +575,11 @@ create table t2(c int);
 	require.Nil(t, f)
 }
 
+func TestCheckVersion(t *testing.T) {
+	require.Error(t, migrate.CheckVersion("1"))
+	require.NoError(t, migrate.CheckVersion(migrate.NewVersion()))
+}
+
 func fileNames(r io.Reader) ([]string, error) {
 	var out []string
 	tr := tar.NewReader(r)
diff --git a/sql/sqlite/migrate_test.go b/sql/sqlite/migrate_test.go
index 9608aedf..a4c2f402 100644
--- a/sql/sqlite/migrate_test.go
+++ b/sql/sqlite/migrate_test.go
@@ -166,6 +166,64 @@ func TestPlanChanges(t *testing.T) {
 				},
 			},
 		},
+		// Add column with constant default value.
+		{
+			changes: []schema.Change{
+				func() schema.Change {
+					users := schema.NewTable("users").
+						AddColumns(schema.NewIntColumn("id", "bigint"))
+					return &schema.ModifyTable{
+						T: users,
+						Changes: []schema.Change{
+							&schema.AddColumn{
+								C: schema.NewStringColumn("name", "text").
+									SetDefault(&schema.Literal{V: "a8m"}),
+							},
+						},
+					}
+				}(),
+			},
+			plan: &migrate.Plan{
+				Reversible:    true,
+				Transactional: true,
+				Changes: []*migrate.Change{
+					{Cmd: "ALTER TABLE `users` ADD COLUMN `name` text NOT NULL DEFAULT 'a8m'", Reverse: "ALTER TABLE `users` DROP COLUMN `name`"},
+				},
+			},
+		},
+		// Add column with expression default value.
+		{
+			changes: []schema.Change{
+				func() schema.Change {
+					users := schema.NewTable("users").
+						AddColumns(
+							schema.NewIntColumn("id", "bigint"),
+							schema.NewStringColumn("name", "text").
+								SetDefault(&schema.RawExpr{X: "NOW()::TEXT"}),
+						)
+					return &schema.ModifyTable{
+						T: users,
+						Changes: []schema.Change{
+							&schema.AddColumn{
+								C: users.Columns[1],
+							},
+						},
+					}
+				}(),
+			},
+			plan: &migrate.Plan{
+				Reversible:    false,
+				Transactional: true,
+				Changes: []*migrate.Change{
+					{Cmd: "PRAGMA foreign_keys = off"},
+					{Cmd: "CREATE TABLE `new_users` (`id` bigint NOT NULL, `name` text NOT NULL DEFAULT (NOW()::TEXT))", Reverse: "DROP TABLE `new_users`"},
+					{Cmd: "INSERT INTO `new_users` (`id`) SELECT `id` FROM `users`"},
+					{Cmd: "DROP TABLE `users`"},
+					{Cmd: "ALTER TABLE `new_users` RENAME TO `users`"},
+					{Cmd: "PRAGMA foreign_keys = on"},
+				},
+			},
+		},
 		// Add VIRTUAL column.
 		{
 			changes: []schema.Change{