Repository: tetratelabs/wazero
Pull request #800 (2022-09-06 06:58:48)

Top-levels CoreFeatures and defaults to 2.0
While compilers should be conservative when targeting WebAssembly Core
features, runtimes should be lenient as otherwise people need to
constantly turn on all features. Currently, most examples have to turn
on 2.0 features because compilers such as AssemblyScript and TinyGo use
them by default. This matches the policy with the reality, and should
make first time use easier.

This top-levels an internal type as `api.CoreFeatures` and defaults to
2.0 as opposed to 1.0, our previous default. This is less cluttered than
the excess of `WithXXX` methods we had prior to implementing all
planned WebAssembly Core Specification 1.0 features.

Finally, this backfills rationale as flat config types were a distinct
decision even if feature set selection muddied the topic.

codefromthecrypt: one interesting thing in the diff, is that this allows defaults to work for AssemblyScript, Go, TinyGo and Zig
codefromthecrypt: passerby's should note that we still individually test specs of both versions. turning this default doesn't risk breaking that.

Patch:
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
index 15ca094b..4ea7721f 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.md
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -1,5 +1,5 @@
 ---
-name: Feature Request
+name: CoreFeature Request
 about: Suggest an idea for wazero to support.
 title: ''
 labels: enhancement
diff --git a/RATIONALE.md b/RATIONALE.md
index 1c2ee273..6549b73c 100644
--- a/RATIONALE.md
+++ b/RATIONALE.md
@@ -272,6 +272,34 @@ wazero took the path of the former design primarily due to:
 There are other reasons such as test and debug being simpler without options: the above list is constrained to conserve
 space. It is accepted that the options pattern is common in Go, which is the main reason for documenting this decision.
 
+### Why aren't config types deeply structured?
+wazero's configuration types cover the three main scopes of WebAssembly use:
+* `RuntimeConfig`: This is the broadest scope, so applies also to compilation
+  and instantiation. Ex. This controls the WebAssembly Specification Version.
+* `CompileConfig`: This affects any compilation related concerns not defined in
+  the binary format. Ex. This controls the allocation of linear memory.
+* `ModuleConfig`: This affects modules instantiated after compilation and what
+  resources are allowed. Ex. This defines how or if STDOUT is captured.
+
+We could nest configuration, for example have `ModuleConfig.SysConfig` instead
+of a flat definition. However, a flat structure is easier to work with and is
+also easy to discover. Unlike the option pattern described earlier, more
+configuration in the interface doesn't taint the package namespace, only
+`ModuleConfig`.
+
+That said, one might ask if we may one day need to structure `ModuleConfig`, as
+it could become cluttered. The main rationale for not doing so up front is that
+once a configuration structure is nested, it is harder to notice configuration
+sprawl. By keeping the config flat, it is easy to see the cognitive load we may
+be adding to our users.
+
+In other words, discomfort adding more configuration is a feature, not a bug.
+We should only add new configuration rarely, and before doing so, ensure it
+will be used. In fact, this is why we support using context fields for
+experimental configuration. By letting users practice, we can find out if a
+configuration was a good idea or not before committing to it, and potentially
+sprawling our types.
+
 ## Why does InstantiateModule call "_start" by default?
 We formerly had functions like `StartWASICommand` that would verify preconditions and start WASI's "_start" command.
 However, this caused confusion because both many languages compiled a WASI dependency, and many did so inconsistently.
diff --git a/api/features.go b/api/features.go
new file mode 100644
index 00000000..e160e47a
--- /dev/null
+++ b/api/features.go
@@ -0,0 +1,212 @@
+package api
+
+import (
+	"fmt"
+	"strings"
+)
+
+// CoreFeatures is a bit flag of WebAssembly Core specification features. See
+// https://github.com/WebAssembly/proposals for proposals and their status.
+//
+// Constants define individual features, such as CoreFeatureMultiValue, or
+// groups of "finished" features, assigned to a WebAssembly Core Specification
+// version, ex. CoreFeaturesV1 or CoreFeaturesV2.
+//
+// Note: Numeric values are not intended to be interpreted except as bit flags.
+type CoreFeatures uint64
+
+// CoreFeaturesV1 are features included in the WebAssembly Core Specification
+// 1.0. As of late 2022, this is the only version that is a Web Standard (W3C
+// Recommendation).
+//
+// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/
+const CoreFeaturesV1 = CoreFeatureMutableGlobal
+
+// CoreFeaturesV2 are features included in the WebAssembly Core Specification
+// 2.0 (20220419). As of late 2022, version 2.0 is a W3C working draft, not yet
+// a Web Standard (W3C Recommendation).
+//
+// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#release-1-1
+const CoreFeaturesV2 = CoreFeaturesV1 |
+	CoreFeatureBulkMemoryOperations |
+	CoreFeatureMultiValue |
+	CoreFeatureNonTrappingFloatToIntConversion |
+	CoreFeatureReferenceTypes |
+	CoreFeatureSignExtensionOps |
+	CoreFeatureSIMD
+
+const (
+	// CoreFeatureBulkMemoryOperations adds instructions modify ranges of
+	// memory or table entries ("bulk-memory-operations"). This is included in
+	// CoreFeaturesV2, but not CoreFeaturesV1.
+	//
+	// Here are the notable effects:
+	//   - Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop`
+	//     instructions.
+	//   - Adds `table.init`, `table.copy` and `elem.drop` instructions.
+	//   - Introduces a "passive" form of element and data segments.
+	//   - Stops checking "active" element and data segment boundaries at
+	//     compile-time, meaning they can error at runtime.
+	//
+	// Note: "bulk-memory-operations" is mixed with the "reference-types"
+	// proposal due to the WebAssembly Working Group merging them
+	// "mutually dependent". Therefore, enabling this feature requires enabling
+	// CoreFeatureReferenceTypes, and vice-versa.
+	//
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
+	// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md and
+	// https://github.com/WebAssembly/spec/pull/1287
+	CoreFeatureBulkMemoryOperations CoreFeatures = 1 << iota
+
+	// CoreFeatureMultiValue enables multiple values ("multi-value"). This is
+	// included in CoreFeaturesV2, but not CoreFeaturesV1.
+	//
+	// Here are the notable effects:
+	//   - Function (`func`) types allow more than one result.
+	//   - Block types (`block`, `loop` and `if`) can be arbitrary function
+	//     types.
+	//
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
+	CoreFeatureMultiValue
+
+	// CoreFeatureMutableGlobal allows globals to be mutable. This is included
+	// in both CoreFeaturesV1 and CoreFeaturesV2.
+	//
+	// When false, an api.Global can never be cast to an api.MutableGlobal, and
+	// any wasm that includes global vars will fail to parse.
+	CoreFeatureMutableGlobal
+
+	// CoreFeatureNonTrappingFloatToIntConversion enables non-trapping
+	// float-to-int conversions ("nontrapping-float-to-int-conversion"). This
+	// is included in CoreFeaturesV2, but not CoreFeaturesV1.
+	//
+	// The only effect of enabling is allowing the following instructions,
+	// which return 0 on NaN instead of panicking.
+	//   - `i32.trunc_sat_f32_s`
+	//   - `i32.trunc_sat_f32_u`
+	//   - `i32.trunc_sat_f64_s`
+	//   - `i32.trunc_sat_f64_u`
+	//   - `i64.trunc_sat_f32_s`
+	//   - `i64.trunc_sat_f32_u`
+	//   - `i64.trunc_sat_f64_s`
+	//   - `i64.trunc_sat_f64_u`
+	//
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md
+	CoreFeatureNonTrappingFloatToIntConversion
+
+	// CoreFeatureReferenceTypes enables various instructions and features
+	// related to table and new reference types. This is included in
+	// CoreFeaturesV2, but not CoreFeaturesV1.
+	//
+	//   - Introduction of new value types: `funcref` and `externref`.
+	//   - Support for the following new instructions:
+	//     - `ref.null`
+	//     - `ref.func`
+	//     - `ref.is_null`
+	//     - `table.fill`
+	//     - `table.get`
+	//     - `table.grow`
+	//     - `table.set`
+	//     - `table.size`
+	//   - Support for multiple tables per module:
+	//     - `call_indirect`, `table.init`, `table.copy` and `elem.drop`
+	//   - Support for instructions can take non-zero table index.
+	//     - Element segments can take non-zero table index.
+	//
+	// Note: "reference-types" is mixed with the "bulk-memory-operations"
+	// proposal due to the WebAssembly Working Group merging them
+	// "mutually dependent". Therefore, enabling this feature requires enabling
+	// CoreFeatureBulkMemoryOperations, and vice-versa.
+	//
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
+	// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md and
+	// https://github.com/WebAssembly/spec/pull/1287
+	CoreFeatureReferenceTypes
+
+	// CoreFeatureSignExtensionOps enables sign extension instructions
+	// ("sign-extension-ops"). This is included in CoreFeaturesV2, but not
+	// CoreFeaturesV1.
+	//
+	// Adds instructions:
+	//   - `i32.extend8_s`
+	//   - `i32.extend16_s`
+	//   - `i64.extend8_s`
+	//   - `i64.extend16_s`
+	//   - `i64.extend32_s`
+	//
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md
+	CoreFeatureSignExtensionOps
+
+	// CoreFeatureSIMD enables the vector value type and vector instructions
+	// (aka SIMD). This is included in CoreFeaturesV2, but not CoreFeaturesV1.
+	//
+	// Note: The instruction list is too long to enumerate in godoc.
+	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
+	CoreFeatureSIMD
+)
+
+// SetEnabled enables or disables the feature or group of features.
+func (f CoreFeatures) SetEnabled(feature CoreFeatures, val bool) CoreFeatures {
+	if val {
+		return f | feature
+	}
+	return f &^ feature
+}
+
+// IsEnabled returns true if the feature (or group of features) is enabled.
+func (f CoreFeatures) IsEnabled(feature CoreFeatures) bool {
+	return f&feature != 0
+}
+
+// RequireEnabled returns an error if the feature (or group of features) is not
+// enabled.
+func (f CoreFeatures) RequireEnabled(feature CoreFeatures) error {
+	if f&feature == 0 {
+		return fmt.Errorf("feature %q is disabled", feature)
+	}
+	return nil
+}
+
+// String implements fmt.Stringer by returning each enabled feature.
+func (f CoreFeatures) String() string {
+	var builder strings.Builder
+	for i := 0; i <= 63; i++ { // cycle through all bits to reduce code and maintenance
+		target := CoreFeatures(1 << i)
+		if f.IsEnabled(target) {
+			if name := featureName(target); name != "" {
+				if builder.Len() > 0 {
+					builder.WriteByte('|')
+				}
+				builder.WriteString(name)
+			}
+		}
+	}
+	return builder.String()
+}
+
+func featureName(f CoreFeatures) string {
+	switch f {
+	case CoreFeatureMutableGlobal:
+		// match https://github.com/WebAssembly/mutable-global
+		return "mutable-global"
+	case CoreFeatureSignExtensionOps:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md
+		return "sign-extension-ops"
+	case CoreFeatureMultiValue:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
+		return "multi-value"
+	case CoreFeatureNonTrappingFloatToIntConversion:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md
+		return "nontrapping-float-to-int-conversion"
+	case CoreFeatureBulkMemoryOperations:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
+		return "bulk-memory-operations"
+	case CoreFeatureReferenceTypes:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md
+		return "reference-types"
+	case CoreFeatureSIMD:
+		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
+		return "simd"
+	}
+	return ""
+}
diff --git a/api/wasm.go b/api/wasm.go
index 1bf33b09..f4cac34a 100644
--- a/api/wasm.go
+++ b/api/wasm.go
@@ -99,7 +99,7 @@ const (
 	//    "f": func(externref uintptr) (resultExternRef uintptr) { return },
 	//  })
 	//
-	// Note: The usage of this type is toggled with WithFeatureBulkMemoryOperations.
+	// Note: The usage of this type is toggled with api.CoreFeatureBulkMemoryOperations.
 	ValueTypeExternref ValueType = 0x6f
 )
 
diff --git a/config.go b/config.go
index 2716da6e..8cd47f6a 100644
--- a/config.go
+++ b/config.go
@@ -22,124 +22,25 @@ import (
 //
 // Ex. To explicitly limit to Wasm Core 1.0 features as opposed to relying on defaults:
 //
-//	rConfig = wazero.NewRuntimeConfig().WithWasmCore1()
+//	rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV1)
 //
 // Note: RuntimeConfig is immutable. Each WithXXX function returns a new instance including the corresponding change.
 type RuntimeConfig interface {
-
-	// WithFeatureBulkMemoryOperations adds instructions modify ranges of memory or table entries
-	// ("bulk-memory-operations"). This defaults to false as the feature was not finished in WebAssembly 1.0.
-	//
-	// Here are the notable effects:
-	//   - Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions.
-	//   - Adds `table.init`, `table.copy` and `elem.drop` instructions.
-	//   - Introduces a "passive" form of element and data segments.
-	//   - Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime.
-	//
-	// Note: "bulk-memory-operations" is mixed with the "reference-types" proposal
-	// due to the WebAssembly Working Group merging them "mutually dependent".
-	// Therefore, enabling this feature results in enabling WithFeatureReferenceTypes, and vice-versa.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
-	// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md and
-	// https://github.com/WebAssembly/spec/pull/1287
-	WithFeatureBulkMemoryOperations(bool) RuntimeConfig
-
-	// WithFeatureMultiValue enables multiple values ("multi-value"). This defaults to false as the feature was not
-	// finished in WebAssembly 1.0 (20191205).
-	//
-	// Here are the notable effects:
-	//   - Function (`func`) types allow more than one result
-	//   - Block types (`block`, `loop` and `if`) can be arbitrary function types
+	// WithCoreFeatures sets the WebAssembly Core specification features this
+	// runtime supports. Defaults to api.CoreFeaturesV2.
 	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
-	WithFeatureMultiValue(bool) RuntimeConfig
-
-	// WithFeatureMutableGlobal allows globals to be mutable. This defaults to true as the feature was finished in
-	// WebAssembly 1.0 (20191205).
+	// Example of disabling a specific feature:
+	//	features := api.CoreFeaturesV2.SetEnabled(api.CoreFeatureMutableGlobal, false)
+	//	rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(features)
 	//
-	// When false, an api.Global can never be cast to an api.MutableGlobal, and any wasm that includes global vars
-	// will fail to parse.
-	WithFeatureMutableGlobal(bool) RuntimeConfig
-
-	// WithFeatureNonTrappingFloatToIntConversion enables non-trapping float-to-int conversions.
-	// ("nontrapping-float-to-int-conversion"). This defaults to false as the feature was not in WebAssembly 1.0.
-	//
-	// The only effect of enabling is allowing the following instructions, which return 0 on NaN instead of panicking.
-	//   - `i32.trunc_sat_f32_s`
-	//   - `i32.trunc_sat_f32_u`
-	//   - `i32.trunc_sat_f64_s`
-	//   - `i32.trunc_sat_f64_u`
-	//   - `i64.trunc_sat_f32_s`
-	//   - `i64.trunc_sat_f32_u`
-	//   - `i64.trunc_sat_f64_s`
-	//   - `i64.trunc_sat_f64_u`
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md
-	WithFeatureNonTrappingFloatToIntConversion(bool) RuntimeConfig
-
-	// WithFeatureReferenceTypes enables various instructions and features related to table and new reference types.
-	//
-	//   - Introduction of new value types: `funcref` and `externref`.
-	//   - Support for the following new instructions:
-	//	 * `ref.null`
-	//	 * `ref.func`
-	//	 * `ref.is_null`
-	//	 * `table.fill`
-	//	 * `table.get`
-	//	 * `table.grow`
-	//	 * `table.set`
-	//	 * `table.size`
-	//   - Support for multiple tables per module:
-	//	 * `call_indirect`, `table.init`, `table.copy` and `elem.drop` instructions can take non-zero table index.
-	//	 * Element segments can take non-zero table index.
-	//
-	// Note: "reference-types" is mixed with the "bulk-memory-operations" proposal
-	// due to the WebAssembly Working Group merging them "mutually dependent".
-	// Therefore, enabling this feature results in enabling WithFeatureBulkMemoryOperations, and vice-versa.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
-	// https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md and
-	// https://github.com/WebAssembly/spec/pull/1287
-	WithFeatureReferenceTypes(enabled bool) RuntimeConfig
-
-	// WithFeatureSignExtensionOps enables sign extension instructions ("sign-extension-ops"). This defaults to false
-	// as the feature was not in WebAssembly 1.0.
+	// # Why default to version 2.0?
 	//
-	// Here are the notable effects:
-	//   - Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s`
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md
-	WithFeatureSignExtensionOps(bool) RuntimeConfig
-
-	// WithFeatureSIMD enables the vector value type and vector instructions (aka SIMD). This defaults to false
-	// as the feature was not in WebAssembly 1.0.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
-	WithFeatureSIMD(bool) RuntimeConfig
-
-	// WithWasmCore1 enables features included in the WebAssembly Core Specification 1.0. Selecting this
-	// overwrites any currently accumulated features with only those included in this W3C recommendation.
-	//
-	// This is default because as of mid 2022, this is the only version that is a Web Standard (W3C Recommendation).
-	//
-	// You can select the latest draft of the WebAssembly Core Specification 2.0 instead via WithWasmCore2. You can
-	// also enable or disable individual features via `WithXXX` methods. Ex.
-	//	rConfig = wazero.NewRuntimeConfig().WithWasmCore1().WithFeatureMutableGlobal(false)
-	//
-	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/
-	WithWasmCore1() RuntimeConfig
-
-	// WithWasmCore2 enables features included in the WebAssembly Core Specification 2.0 (20220419). Selecting this
-	// overwrites any currently accumulated features with only those included in this W3C working draft.
-	//
-	// This is not default because it is not yet incomplete and also not yet a Web Standard (W3C Recommendation).
-	//
-	// Even after selecting this, you can enable or disable individual features via `WithXXX` methods. Ex.
-	//	rConfig = wazero.NewRuntimeConfig().WithWasmCore2().WithFeatureMutableGlobal(false)
-	//
-	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/
-	WithWasmCore2() RuntimeConfig
+	// Many compilers that target WebAssembly require features after
+	// api.CoreFeaturesV1 by default. For example, TinyGo v0.24+ requires
+	// api.CoreFeatureBulkMemoryOperations. To avoid runtime errors, wazero
+	// defaults to api.CoreFeaturesV2, even though it is not yet a Web
+	// Standard (REC).
+	WithCoreFeatures(api.CoreFeatures) RuntimeConfig
 }
 
 // NewRuntimeConfig returns a RuntimeConfig using the compiler if it is supported in this environment,
@@ -149,14 +50,14 @@ func NewRuntimeConfig() RuntimeConfig {
 }
 
 type runtimeConfig struct {
-	enabledFeatures wasm.Features
+	enabledFeatures api.CoreFeatures
 	isInterpreter   bool
-	newEngine       func(context.Context, wasm.Features) wasm.Engine
+	newEngine       func(context.Context, api.CoreFeatures) wasm.Engine
 }
 
 // engineLessConfig helps avoid copy/pasting the wrong defaults.
 var engineLessConfig = &runtimeConfig{
-	enabledFeatures: wasm.Features20191205,
+	enabledFeatures: api.CoreFeaturesV2,
 }
 
 // NewRuntimeConfigCompiler compiles WebAssembly modules into
@@ -193,74 +94,14 @@ func (c *runtimeConfig) clone() *runtimeConfig {
 	return &ret
 }
 
-// WithFeatureBulkMemoryOperations implements RuntimeConfig.WithFeatureBulkMemoryOperations
-func (c *runtimeConfig) WithFeatureBulkMemoryOperations(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled)
-	// bulk-memory-operations proposal is mutually-dependant with reference-types proposal.
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureReferenceTypes, enabled)
-	return ret
-}
-
-// WithFeatureMultiValue implements RuntimeConfig.WithFeatureMultiValue
-func (c *runtimeConfig) WithFeatureMultiValue(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMultiValue, enabled)
-	return ret
-}
-
-// WithFeatureMutableGlobal implements RuntimeConfig.WithFeatureMutableGlobal
-func (c *runtimeConfig) WithFeatureMutableGlobal(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMutableGlobal, enabled)
-	return ret
-}
-
-// WithFeatureNonTrappingFloatToIntConversion implements RuntimeConfig.WithFeatureNonTrappingFloatToIntConversion
-func (c *runtimeConfig) WithFeatureNonTrappingFloatToIntConversion(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureNonTrappingFloatToIntConversion, enabled)
-	return ret
-}
-
-// WithFeatureReferenceTypes implements RuntimeConfig.WithFeatureReferenceTypes
-func (c *runtimeConfig) WithFeatureReferenceTypes(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureReferenceTypes, enabled)
-	// reference-types proposal is mutually-dependant with bulk-memory-operations proposal.
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled)
-	return ret
-}
-
-// WithFeatureSignExtensionOps implements RuntimeConfig.WithFeatureSignExtensionOps
-func (c *runtimeConfig) WithFeatureSignExtensionOps(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSignExtensionOps, enabled)
-	return ret
-}
-
-// WithFeatureSIMD implements RuntimeConfig.WithFeatureSIMD
-func (c *runtimeConfig) WithFeatureSIMD(enabled bool) RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSIMD, enabled)
-	return ret
-}
-
-// WithWasmCore1 implements RuntimeConfig.WithWasmCore1
-func (c *runtimeConfig) WithWasmCore1() RuntimeConfig {
-	ret := c.clone()
-	ret.enabledFeatures = wasm.Features20191205
-	return ret
-}
-
-// WithWasmCore2 implements RuntimeConfig.WithWasmCore2
-func (c *runtimeConfig) WithWasmCore2() RuntimeConfig {
+// WithCoreFeatures implements RuntimeConfig.WithCoreFeatures
+func (c *runtimeConfig) WithCoreFeatures(features api.CoreFeatures) RuntimeConfig {
 	ret := c.clone()
-	ret.enabledFeatures = wasm.Features20220419
+	ret.enabledFeatures = features
 	return ret
 }
 
-// CompiledModule is a WebAssembly 1.0 module ready to be instantiated (Runtime.InstantiateModule) as an api.Module.
+// CompiledModule is a WebAssembly module ready to be instantiated (Runtime.InstantiateModule) as an api.Module.
 //
 // In WebAssembly terminology, this is a decoded, validated, and possibly also compiled module. wazero avoids using
 // the name "Module" for both before and after instantiation as the name conflation has caused confusion.
@@ -327,7 +168,7 @@ func (c *compiledModule) ExportedFunctions() map[string]api.FunctionDefinition {
 // CompileConfig allows you to override what was decoded from wasm, prior to compilation (ModuleBuilder.Compile or
 // Runtime.CompileModule).
 //
-// For example, WithMemorySizer allows you to override memoryc size that doesn't match your requirements.
+// For example, WithMemorySizer allows you to override memory size that doesn't match your requirements.
 //
 // Note: CompileConfig is immutable. Each WithXXX function returns a new instance including the corresponding change.
 type CompileConfig interface {
diff --git a/examples/allocation/tinygo/greet.go b/examples/allocation/tinygo/greet.go
index fcedabc2..cc7215e9 100644
--- a/examples/allocation/tinygo/greet.go
+++ b/examples/allocation/tinygo/greet.go
@@ -26,9 +26,7 @@ func main() {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of any version of TinyGo, including 0.24+.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Instantiate a Go-defined module named "env" that exports a function to
diff --git a/examples/allocation/zig/greet.go b/examples/allocation/zig/greet.go
index 063e0e83..179b9622 100644
--- a/examples/allocation/zig/greet.go
+++ b/examples/allocation/zig/greet.go
@@ -31,10 +31,7 @@ func run() error {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// Enable WebAssembly 2.0 support.
-		WithWasmCore2(),
-	)
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Instantiate a Go-defined module named "env" that exports a function to
diff --git a/examples/basic/add.go b/examples/basic/add.go
index 99d9be0a..d45c69cd 100644
--- a/examples/basic/add.go
+++ b/examples/basic/add.go
@@ -29,9 +29,7 @@ func main() {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of any version of TinyGo, including 0.24+.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Instantiate WASI, which implements host functions needed for TinyGo to
diff --git a/examples/multiple-results/multiple-results.go b/examples/multiple-results/multiple-results.go
index 15d43fa5..2c63f503 100644
--- a/examples/multiple-results/multiple-results.go
+++ b/examples/multiple-results/multiple-results.go
@@ -43,11 +43,8 @@ func main() {
 		log.Panicln(err)
 	}
 
-	// wazero enables WebAssembly 1.0 by default. Opt-in to other features:
-	runtimeWithMultiValue := wazero.NewRuntimeWithConfig(
-		ctx, wazero.NewRuntimeConfig().WithFeatureMultiValue(true),
-		// ^^ Note: WebAssembly 2.0 (WithWasmCore2) includes "multi-value".
-	)
+	// wazero enables WebAssembly Core Specification 2.0 features by default.
+	runtimeWithMultiValue := wazero.NewRuntime(ctx)
 
 	// Add a module that uses multiple results values
 
diff --git a/imports/assemblyscript/example/assemblyscript.go b/imports/assemblyscript/example/assemblyscript.go
index ee27c049..524b8d47 100644
--- a/imports/assemblyscript/example/assemblyscript.go
+++ b/imports/assemblyscript/example/assemblyscript.go
@@ -27,9 +27,7 @@ func main() {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	// Use WebAssembly 2.0 because AssemblyScript uses some >1.0 features.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Instantiate a module implementing functions used by AssemblyScript.
diff --git a/imports/go/example/stars.go b/imports/go/example/stars.go
index 561576f8..4249ac8e 100644
--- a/imports/go/example/stars.go
+++ b/imports/go/example/stars.go
@@ -31,9 +31,7 @@ func main() {
 	}
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of gojs.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Combine the above into our baseline config, overriding defaults.
diff --git a/imports/wasi_snapshot_preview1/example/cat.go b/imports/wasi_snapshot_preview1/example/cat.go
index bf8513fe..564e36b4 100644
--- a/imports/wasi_snapshot_preview1/example/cat.go
+++ b/imports/wasi_snapshot_preview1/example/cat.go
@@ -42,9 +42,7 @@ func main() {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of any version of TinyGo, including 0.24+.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Since wazero uses fs.FS, we can use standard libraries to do things like trim the leading path.
diff --git a/internal/engine/compiler/engine.go b/internal/engine/compiler/engine.go
index e2ebe86a..773cf176 100644
--- a/internal/engine/compiler/engine.go
+++ b/internal/engine/compiler/engine.go
@@ -9,6 +9,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/buildoptions"
 	"github.com/tetratelabs/wazero/internal/compilationcache"
 	"github.com/tetratelabs/wazero/internal/platform"
@@ -22,7 +23,7 @@ import (
 type (
 	// engine is a Compiler implementation of wasm.Engine
 	engine struct {
-		enabledFeatures wasm.Features
+		enabledFeatures api.CoreFeatures
 		codes           map[wasm.ModuleID][]*code // guarded by mutex.
 		Cache           compilationcache.Cache
 		mux             sync.RWMutex
@@ -682,11 +683,11 @@ func (ce *callEngine) deferredOnCall(recovered interface{}) (err error) {
 	return
 }
 
-func NewEngine(ctx context.Context, enabledFeatures wasm.Features) wasm.Engine {
+func NewEngine(ctx context.Context, enabledFeatures api.CoreFeatures) wasm.Engine {
 	return newEngine(ctx, enabledFeatures)
 }
 
-func newEngine(ctx context.Context, enabledFeatures wasm.Features) *engine {
+func newEngine(ctx context.Context, enabledFeatures api.CoreFeatures) *engine {
 	var wazeroVersion string
 	if v := ctx.Value(version.WazeroVersionKey{}); v != nil {
 		wazeroVersion = v.(string)
@@ -884,7 +885,7 @@ func compileGoDefinedHostFunction(ir *wazeroir.CompilationResult) (*code, error)
 	return &code{codeSegment: c}, nil
 }
 
-func compileWasmFunction(_ wasm.Features, ir *wazeroir.CompilationResult) (*code, error) {
+func compileWasmFunction(_ api.CoreFeatures, ir *wazeroir.CompilationResult) (*code, error) {
 	compiler, err := newCompiler(ir)
 	if err != nil {
 		return nil, fmt.Errorf("failed to initialize assembly builder: %w", err)
diff --git a/internal/engine/interpreter/interpreter.go b/internal/engine/interpreter/interpreter.go
index 72d57c59..f18d1e9f 100644
--- a/internal/engine/interpreter/interpreter.go
+++ b/internal/engine/interpreter/interpreter.go
@@ -11,6 +11,7 @@ import (
 	"sync"
 	"unsafe"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/internal/moremath"
 	"github.com/tetratelabs/wazero/internal/wasm"
@@ -27,12 +28,12 @@ var callStackCeiling = 2000
 
 // engine is an interpreter implementation of wasm.Engine
 type engine struct {
-	enabledFeatures wasm.Features
+	enabledFeatures api.CoreFeatures
 	codes           map[wasm.ModuleID][]*code // guarded by mutex.
 	mux             sync.RWMutex
 }
 
-func NewEngine(_ context.Context, enabledFeatures wasm.Features) wasm.Engine {
+func NewEngine(_ context.Context, enabledFeatures api.CoreFeatures) wasm.Engine {
 	return &engine{
 		enabledFeatures: enabledFeatures,
 		codes:           map[wasm.ModuleID][]*code{},
diff --git a/internal/wasm/binary/const_expr.go b/internal/wasm/binary/const_expr.go
index e4a8bb56..8269d317 100644
--- a/internal/wasm/binary/const_expr.go
+++ b/internal/wasm/binary/const_expr.go
@@ -4,12 +4,13 @@ import (
 	"bytes"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/ieee754"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
-func decodeConstantExpression(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm.ConstantExpression, error) {
+func decodeConstantExpression(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.ConstantExpression, error) {
 	b, err := r.ReadByte()
 	if err != nil {
 		return nil, fmt.Errorf("read opcode: %v", err)
@@ -33,7 +34,7 @@ func decodeConstantExpression(r *bytes.Reader, enabledFeatures wasm.Features) (*
 	case wasm.OpcodeGlobalGet:
 		_, _, err = leb128.DecodeUint32(r)
 	case wasm.OpcodeRefNull:
-		if err := enabledFeatures.Require(wasm.FeatureBulkMemoryOperations); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 			return nil, fmt.Errorf("ref.null is not supported as %w", err)
 		}
 		reftype, err := r.ReadByte()
@@ -43,13 +44,13 @@ func decodeConstantExpression(r *bytes.Reader, enabledFeatures wasm.Features) (*
 			return nil, fmt.Errorf("invalid type for ref.null: 0x%x", reftype)
 		}
 	case wasm.OpcodeRefFunc:
-		if err := enabledFeatures.Require(wasm.FeatureBulkMemoryOperations); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 			return nil, fmt.Errorf("ref.func is not supported as %w", err)
 		}
 		// Parsing index.
 		_, _, err = leb128.DecodeUint32(r)
 	case wasm.OpcodeVecPrefix:
-		if err := enabledFeatures.Require(wasm.FeatureSIMD); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureSIMD); err != nil {
 			return nil, fmt.Errorf("vector instructions are not supported as %w", err)
 		}
 		opcode, err = r.ReadByte()
diff --git a/internal/wasm/binary/data.go b/internal/wasm/binary/data.go
index 74e08089..aa9f6394 100644
--- a/internal/wasm/binary/data.go
+++ b/internal/wasm/binary/data.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"io"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -23,14 +24,14 @@ const (
 	dataSegmentPrefixActiveWithMemoryIndex dataSegmentPrefix = 0x2
 )
 
-func decodeDataSegment(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm.DataSegment, error) {
+func decodeDataSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.DataSegment, error) {
 	dataSegmentPrefx, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("read data segment prefix: %w", err)
 	}
 
 	if dataSegmentPrefx != dataSegmentPrefixActive {
-		if err := enabledFeatures.Require(wasm.FeatureBulkMemoryOperations); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 			return nil, fmt.Errorf("non-zero prefix for data segment is invalid as %w", err)
 		}
 	}
diff --git a/internal/wasm/binary/decoder.go b/internal/wasm/binary/decoder.go
index 6bf8284d..354ce514 100644
--- a/internal/wasm/binary/decoder.go
+++ b/internal/wasm/binary/decoder.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -14,7 +15,7 @@ import (
 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0
 func DecodeModule(
 	binary []byte,
-	enabledFeatures wasm.Features,
+	enabledFeatures api.CoreFeatures,
 	memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
 ) (*wasm.Module, error) {
 	r := bytes.NewReader(binary)
@@ -103,7 +104,7 @@ func DecodeModule(
 		case wasm.SectionIDData:
 			m.DataSection, err = decodeDataSection(r, enabledFeatures)
 		case wasm.SectionIDDataCount:
-			if err := enabledFeatures.Require(wasm.FeatureBulkMemoryOperations); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 				return nil, fmt.Errorf("data count section not supported as %v", err)
 			}
 			m.DataCountSection, err = decodeDataCountSection(r)
diff --git a/internal/wasm/binary/element.go b/internal/wasm/binary/element.go
index a0841b91..c776645c 100644
--- a/internal/wasm/binary/element.go
+++ b/internal/wasm/binary/element.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -37,7 +38,7 @@ func decodeElementInitValueVector(r *bytes.Reader) ([]*wasm.Index, error) {
 	return vec, nil
 }
 
-func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures wasm.Features) ([]*wasm.Index, error) {
+func decodeElementConstExprVector(r *bytes.Reader, elemType wasm.RefType, enabledFeatures api.CoreFeatures) ([]*wasm.Index, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("failed to get the size of constexpr vector: %w", err)
@@ -83,7 +84,7 @@ func decodeElementRefType(r *bytes.Reader) (ret wasm.RefType, err error) {
 const (
 	// The prefix is explained at https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#element-section
 
-	// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before FeatureBulkMemoryOperations.
+	// elementSegmentPrefixLegacy is the legacy prefix and is only valid one before CoreFeatureBulkMemoryOperations.
 	elementSegmentPrefixLegacy = iota
 	// elementSegmentPrefixPassiveFuncrefValueVector is the passive element whose indexes are encoded as vec(varint), and reftype is fixed to funcref.
 	elementSegmentPrefixPassiveFuncrefValueVector
@@ -101,14 +102,14 @@ const (
 	elementSegmentPrefixDeclarativeConstExprVector
 )
 
-func decodeElementSegment(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm.ElementSegment, error) {
+func decodeElementSegment(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.ElementSegment, error) {
 	prefix, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("read element prefix: %w", err)
 	}
 
 	if prefix != elementSegmentPrefixLegacy {
-		if err := enabledFeatures.Require(wasm.FeatureBulkMemoryOperations); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 			return nil, fmt.Errorf("non-zero prefix for element segment is invalid as %w", err)
 		}
 	}
@@ -157,7 +158,7 @@ func decodeElementSegment(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm
 		}
 
 		if tableIndex != 0 {
-			if err := enabledFeatures.Require(wasm.FeatureReferenceTypes); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 				return nil, fmt.Errorf("table index must be zero but was %d: %w", tableIndex, err)
 			}
 		}
@@ -236,7 +237,7 @@ func decodeElementSegment(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm
 		}
 
 		if tableIndex != 0 {
-			if err := enabledFeatures.Require(wasm.FeatureReferenceTypes); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 				return nil, fmt.Errorf("table index must be zero but was %d: %w", tableIndex, err)
 			}
 		}
diff --git a/internal/wasm/binary/function.go b/internal/wasm/binary/function.go
index 3a246e2f..fdf80201 100644
--- a/internal/wasm/binary/function.go
+++ b/internal/wasm/binary/function.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -55,7 +56,7 @@ func encodeFunctionType(t *wasm.FunctionType) []byte {
 	return append(data, encodeValTypes(t.Results)...)
 }
 
-func decodeFunctionType(enabledFeatures wasm.Features, r *bytes.Reader) (*wasm.FunctionType, error) {
+func decodeFunctionType(enabledFeatures api.CoreFeatures, r *bytes.Reader) (*wasm.FunctionType, error) {
 	b, err := r.ReadByte()
 	if err != nil {
 		return nil, fmt.Errorf("read leading byte: %w", err)
@@ -82,7 +83,7 @@ func decodeFunctionType(enabledFeatures wasm.Features, r *bytes.Reader) (*wasm.F
 
 	// Guard >1.0 feature multi-value
 	if resultCount > 1 {
-		if err = enabledFeatures.Require(wasm.FeatureMultiValue); err != nil {
+		if err = enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
 			return nil, fmt.Errorf("multiple result types invalid as %v", err)
 		}
 	}
diff --git a/internal/wasm/binary/global.go b/internal/wasm/binary/global.go
index 59ceb254..4da56562 100644
--- a/internal/wasm/binary/global.go
+++ b/internal/wasm/binary/global.go
@@ -4,13 +4,14 @@ import (
 	"bytes"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
 // decodeGlobal returns the api.Global decoded with the WebAssembly 1.0 (20191205) Binary Format.
 //
 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-global
-func decodeGlobal(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm.Global, error) {
+func decodeGlobal(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.Global, error) {
 	gt, err := decodeGlobalType(r)
 	if err != nil {
 		return nil, err
diff --git a/internal/wasm/binary/import.go b/internal/wasm/binary/import.go
index d561dd4f..d34c290d 100644
--- a/internal/wasm/binary/import.go
+++ b/internal/wasm/binary/import.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -12,7 +13,7 @@ func decodeImport(
 	r *bytes.Reader,
 	idx uint32,
 	memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
-	enabledFeatures wasm.Features,
+	enabledFeatures api.CoreFeatures,
 ) (i *wasm.Import, err error) {
 	i = &wasm.Import{}
 	if i.Module, _, err = decodeUTF8(r, "import module"); err != nil {
diff --git a/internal/wasm/binary/section.go b/internal/wasm/binary/section.go
index 47ad357c..3711778b 100644
--- a/internal/wasm/binary/section.go
+++ b/internal/wasm/binary/section.go
@@ -5,11 +5,12 @@ import (
 	"fmt"
 	"io"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
-func decodeTypeSection(enabledFeatures wasm.Features, r *bytes.Reader) ([]*wasm.FunctionType, error) {
+func decodeTypeSection(enabledFeatures api.CoreFeatures, r *bytes.Reader) ([]*wasm.FunctionType, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("get size of vector: %w", err)
@@ -27,7 +28,7 @@ func decodeTypeSection(enabledFeatures wasm.Features, r *bytes.Reader) ([]*wasm.
 func decodeImportSection(
 	r *bytes.Reader,
 	memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
-	enabledFeatures wasm.Features,
+	enabledFeatures api.CoreFeatures,
 ) ([]*wasm.Import, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
@@ -58,13 +59,13 @@ func decodeFunctionSection(r *bytes.Reader) ([]uint32, error) {
 	return result, err
 }
 
-func decodeTableSection(r *bytes.Reader, enabledFeatures wasm.Features) ([]*wasm.Table, error) {
+func decodeTableSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*wasm.Table, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("error reading size")
 	}
 	if vs > 1 {
-		if err := enabledFeatures.Require(wasm.FeatureReferenceTypes); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 			return nil, fmt.Errorf("at most one table allowed in module as %w", err)
 		}
 	}
@@ -95,7 +96,7 @@ func decodeMemorySection(
 	return decodeMemory(r, memorySizer)
 }
 
-func decodeGlobalSection(r *bytes.Reader, enabledFeatures wasm.Features) ([]*wasm.Global, error) {
+func decodeGlobalSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*wasm.Global, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("get size of vector: %w", err)
@@ -141,7 +142,7 @@ func decodeStartSection(r *bytes.Reader) (*wasm.Index, error) {
 	return &vs, nil
 }
 
-func decodeElementSection(r *bytes.Reader, enabledFeatures wasm.Features) ([]*wasm.ElementSegment, error) {
+func decodeElementSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*wasm.ElementSegment, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("get size of vector: %w", err)
@@ -171,7 +172,7 @@ func decodeCodeSection(r *bytes.Reader) ([]*wasm.Code, error) {
 	return result, nil
 }
 
-func decodeDataSection(r *bytes.Reader, enabledFeatures wasm.Features) ([]*wasm.DataSegment, error) {
+func decodeDataSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*wasm.DataSegment, error) {
 	vs, _, err := leb128.DecodeUint32(r)
 	if err != nil {
 		return nil, fmt.Errorf("get size of vector: %w", err)
diff --git a/internal/wasm/binary/table.go b/internal/wasm/binary/table.go
index 113826ba..8aad3fff 100644
--- a/internal/wasm/binary/table.go
+++ b/internal/wasm/binary/table.go
@@ -4,20 +4,21 @@ import (
 	"bytes"
 	"fmt"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
 // decodeTable returns the wasm.Table decoded with the WebAssembly 1.0 (20191205) Binary Format.
 //
 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-table
-func decodeTable(r *bytes.Reader, enabledFeatures wasm.Features) (*wasm.Table, error) {
+func decodeTable(r *bytes.Reader, enabledFeatures api.CoreFeatures) (*wasm.Table, error) {
 	tableType, err := r.ReadByte()
 	if err != nil {
 		return nil, fmt.Errorf("read leading byte: %v", err)
 	}
 
 	if tableType != wasm.RefTypeFuncref {
-		if err := enabledFeatures.Require(wasm.FeatureReferenceTypes); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 			return nil, fmt.Errorf("table type funcref is invalid: %w", err)
 		}
 	}
diff --git a/internal/wasm/engine.go b/internal/wasm/engine.go
index 5a9a3c6c..e4bd9fe9 100644
--- a/internal/wasm/engine.go
+++ b/internal/wasm/engine.go
@@ -72,7 +72,7 @@ type TableInitEntry struct {
 }
 
 // ErrElementOffsetOutOfBounds is the error raised when the active element offset exceeds the table length.
-// Before FeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
+// Before CoreFeatureReferenceTypes, this was checked statically before instantiation, after the proposal,
 // this must be raised as runtime error (as in assert_trap in spectest), not even an instantiation error.
 //
 // See https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
diff --git a/internal/wasm/features.go b/internal/wasm/features.go
deleted file mode 100644
index c52faad7..00000000
--- a/internal/wasm/features.go
+++ /dev/null
@@ -1,169 +0,0 @@
-package wasm
-
-import (
-	"fmt"
-	"strings"
-)
-
-// Features are the currently enabled features.
-//
-// Note: This is a bit flag until we have too many (>63). Flags are simpler to manage in multiple places than a map.
-type Features uint64
-
-// Features20191205 include those finished in WebAssembly 1.0 (20191205).
-//
-// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205
-const Features20191205 = FeatureMutableGlobal
-
-// Features20220419 include those finished in WebAssembly 2.0 (20220419).
-//
-// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#release-1-1
-const Features20220419 = Features20191205 |
-	FeatureBulkMemoryOperations |
-	FeatureMultiValue |
-	FeatureNonTrappingFloatToIntConversion |
-	FeatureReferenceTypes |
-	FeatureSignExtensionOps |
-	FeatureSIMD
-
-const (
-	// FeatureBulkMemoryOperations decides if parsing should succeed on the following instructions:
-	//
-	// * [ OpcodeMiscPrefix, OpcodeMiscMemoryInit]
-	// * [ OpcodeMiscPrefix, OpcodeMiscDataDrop]
-	// * [ OpcodeMiscPrefix, OpcodeMiscMemoryCopy]
-	// * [ OpcodeMiscPrefix, OpcodeMiscMemoryFill]
-	// * [ OpcodeMiscPrefix, OpcodeMiscTableInit]
-	// * [ OpcodeMiscPrefix, OpcodeMiscElemDrop]
-	// * [ OpcodeMiscPrefix, OpcodeMiscTableCopy]
-	//
-	// Also, if the parsing should succeed with the presence of SectionIDDataCount.
-	//
-	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
-	FeatureBulkMemoryOperations Features = 1 << iota
-
-	// FeatureMultiValue decides if parsing should succeed on the following:
-	//
-	// * FunctionType.Results length greater than one.
-	// * `block`, `loop` and `if` can be arbitrary function types.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
-	FeatureMultiValue
-
-	// FeatureMutableGlobal decides if global vars are allowed to be imported or exported (ExternTypeGlobal)
-	// See https://github.com/WebAssembly/mutable-global
-	FeatureMutableGlobal
-
-	// FeatureNonTrappingFloatToIntConversion decides if parsing should succeed on the following instructions:
-	//
-	// * [ OpcodeMiscPrefix, OpcodeMiscI32TruncSatF32S]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI32TruncSatF32U]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI64TruncSatF32S]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI64TruncSatF32U]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI32TruncSatF64S]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI32TruncSatF64U]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI64TruncSatF64S]
-	// * [ OpcodeMiscPrefix, OpcodeMiscI64TruncSatF64U]
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md
-	FeatureNonTrappingFloatToIntConversion
-
-	// FeatureReferenceTypes enables various features related to reference types and tables.
-	// * Introduction of new values types: RefTypeFuncref and RefTypeExternref
-	// * Support for the following opcodes:
-	//  * OpcodeRefNull
-	//  * OpcodeRefIsNull
-	//  * OpcodeRefFunc
-	// 	* OpcodeTableGet
-	// 	* OpcodeTableSet
-	// 	* [ OpcodeMiscPrefix, OpcodeMiscTableFill]
-	// 	* [ OpcodeMiscPrefix, OpcodeMiscTableGrow]
-	// 	* [ OpcodeMiscPrefix, OpcodeMiscTableSize]
-	// * Support for multiple tables per module:
-	// 	* OpcodeCallIndirect, OpcodeTableInit, and OpcodeElemDrop can take non-zero table indexes.
-	// 	* Element segments can take non-zero table index.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md
-	FeatureReferenceTypes
-
-	// FeatureSignExtensionOps decides if parsing should succeed on the following instructions:
-	//
-	// * OpcodeI32Extend8S
-	// * OpcodeI32Extend16S
-	// * OpcodeI64Extend8S
-	// * OpcodeI64Extend16S
-	// * OpcodeI64Extend32S
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md
-	FeatureSignExtensionOps
-
-	// FeatureSIMD enables the vector value type and vector instructions.
-	//
-	// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
-	FeatureSIMD
-)
-
-// Set assigns the value for the given feature.
-func (f Features) Set(feature Features, val bool) Features {
-	if val {
-		return f | feature
-	}
-	return f &^ feature
-}
-
-// Get returns the value of the given feature.
-func (f Features) Get(feature Features) bool {
-	return f&feature != 0
-}
-
-// Require fails with a configuration error if the given feature is not enabled
-func (f Features) Require(feature Features) error {
-	if f&feature == 0 {
-		return fmt.Errorf("feature %q is disabled", feature)
-	}
-	return nil
-}
-
-// String implements fmt.Stringer by returning each enabled feature.
-func (f Features) String() string {
-	var builder strings.Builder
-	for i := 0; i <= 63; i++ { // cycle through all bits to reduce code and maintenance
-		target := Features(1 << i)
-		if f.Get(target) {
-			if name := featureName(target); name != "" {
-				if builder.Len() > 0 {
-					builder.WriteByte('|')
-				}
-				builder.WriteString(name)
-			}
-		}
-	}
-	return builder.String()
-}
-
-func featureName(f Features) string {
-	switch f {
-	case FeatureMutableGlobal:
-		// match https://github.com/WebAssembly/mutable-global
-		return "mutable-global"
-	case FeatureSignExtensionOps:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md
-		return "sign-extension-ops"
-	case FeatureMultiValue:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
-		return "multi-value"
-	case FeatureNonTrappingFloatToIntConversion:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md
-		return "nontrapping-float-to-int-conversion"
-	case FeatureBulkMemoryOperations:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/bulk-memory-operations/Overview.md
-		return "bulk-memory-operations"
-	case FeatureReferenceTypes:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/reference-types/Overview.md
-		return "reference-types"
-	case FeatureSIMD:
-		// match https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/simd/SIMD.md
-		return "simd"
-	}
-	return ""
-}
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index 634a976f..668527c3 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -27,7 +27,7 @@ const maximumValuesOnStack = 1 << 27
 //
 // Returns an error if the instruction sequence is not valid,
 // or potentially it can exceed the maximum number of values on the stack.
-func (m *Module) validateFunction(enabledFeatures Features, idx Index, functions []Index,
+func (m *Module) validateFunction(enabledFeatures api.CoreFeatures, idx Index, functions []Index,
 	globals []*GlobalType, memory *Memory, tables []*Table, declaredFunctionIndexes map[Index]struct{}) error {
 	return m.validateFunctionWithMaxStackValues(enabledFeatures, idx, functions, globals, memory, tables, maximumValuesOnStack, declaredFunctionIndexes)
 }
@@ -54,7 +54,7 @@ func readMemArg(pc uint64, body []byte) (align, offset uint32, read uint64, err
 //
 // * maxStackValues is the maximum height of values stack which the target is allowed to reach.
 func (m *Module) validateFunctionWithMaxStackValues(
-	enabledFeatures Features,
+	enabledFeatures api.CoreFeatures,
 	idx Index,
 	functions []Index,
 	globals []*GlobalType,
@@ -478,7 +478,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				copy(defaultLabelType, lnLabel.blockType.Params)
 			}
 
-			if enabledFeatures.Get(FeatureReferenceTypes) {
+			if enabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
 				// As of reference-types proposal, br_table on unreachable state
 				// can choose unknown types for expected parameter types for each label.
 				// https://github.com/WebAssembly/reference-types/pull/116
@@ -562,7 +562,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			}
 			pc += num - 1
 			if tableIndex != 0 {
-				if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 					return fmt.Errorf("table index must be zero but was %d: %w", tableIndex, err)
 				}
 			}
@@ -785,7 +785,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				}
 				valueTypeStack.push(ValueTypeF64)
 			case OpcodeI32Extend8S, OpcodeI32Extend16S:
-				if err := enabledFeatures.Require(FeatureSignExtensionOps); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureSignExtensionOps); err != nil {
 					return fmt.Errorf("%s invalid as %v", instructionNames[op], err)
 				}
 				if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil {
@@ -793,7 +793,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				}
 				valueTypeStack.push(ValueTypeI32)
 			case OpcodeI64Extend8S, OpcodeI64Extend16S, OpcodeI64Extend32S:
-				if err := enabledFeatures.Require(FeatureSignExtensionOps); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureSignExtensionOps); err != nil {
 					return fmt.Errorf("%s invalid as %v", instructionNames[op], err)
 				}
 				if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil {
@@ -804,7 +804,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				return fmt.Errorf("invalid numeric instruction 0x%x", op)
 			}
 		} else if op >= OpcodeRefNull && op <= OpcodeRefFunc {
-			if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 				return fmt.Errorf("%s invalid as %v", instructionNames[op], err)
 			}
 			switch op {
@@ -839,7 +839,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				valueTypeStack.push(ValueTypeFuncref)
 			}
 		} else if op == OpcodeTableGet || op == OpcodeTableSet {
-			if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 				return fmt.Errorf("%s is invalid as %v", InstructionName(op), err)
 			}
 			pc++
@@ -872,7 +872,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			// and the second byte determines the actual instruction.
 			miscOpcode := body[pc]
 			if miscOpcode >= OpcodeMiscI32TruncSatF32S && miscOpcode <= OpcodeMiscI64TruncSatF64U {
-				if err := enabledFeatures.Require(FeatureNonTrappingFloatToIntConversion); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureNonTrappingFloatToIntConversion); err != nil {
 					return fmt.Errorf("%s invalid as %v", miscInstructionNames[miscOpcode], err)
 				}
 				var inType, outType ValueType
@@ -891,7 +891,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 				}
 				valueTypeStack.push(outType)
 			} else if miscOpcode >= OpcodeMiscMemoryInit && miscOpcode <= OpcodeMiscTableCopy {
-				if err := enabledFeatures.Require(FeatureBulkMemoryOperations); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil {
 					return fmt.Errorf("%s invalid as %v", miscInstructionNames[miscOpcode], err)
 				}
 				var params []ValueType
@@ -972,7 +972,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 						return fmt.Errorf("failed to read source table index for %s: %v", MiscInstructionName(miscOpcode), err)
 					}
 					if tableIndex != 0 {
-						if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+						if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 							return fmt.Errorf("source table index must be zero for %s as %v", MiscInstructionName(miscOpcode), err)
 						}
 					}
@@ -1005,7 +1005,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 						return fmt.Errorf("failed to read destination table index for %s: %v", MiscInstructionName(miscOpcode), err)
 					}
 					if dstTableIndex != 0 {
-						if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+						if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 							return fmt.Errorf("destination table index must be zero for %s as %v", MiscInstructionName(miscOpcode), err)
 						}
 					}
@@ -1019,7 +1019,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 						return fmt.Errorf("failed to read source table index for %s: %v", MiscInstructionName(miscOpcode), err)
 					}
 					if srcTableIndex != 0 {
-						if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+						if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 							return fmt.Errorf("source table index must be zero for %s as %v", MiscInstructionName(miscOpcode), err)
 						}
 					}
@@ -1040,7 +1040,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 					}
 				}
 			} else if miscOpcode >= OpcodeMiscTableGrow && miscOpcode <= OpcodeMiscTableFill {
-				if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 					return fmt.Errorf("%s invalid as %v", miscInstructionNames[miscOpcode], err)
 				}
 
@@ -1079,7 +1079,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			// Vector instructions come with two bytes where the first byte is always OpcodeVecPrefix,
 			// and the second byte determines the actual instruction.
 			vecOpcode := body[pc]
-			if err := enabledFeatures.Require(FeatureSIMD); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureSIMD); err != nil {
 				return fmt.Errorf("%s invalid as %v", vectorInstructionName[vecOpcode], err)
 			}
 
@@ -1519,7 +1519,7 @@ func (m *Module) validateFunctionWithMaxStackValues(
 			}
 
 			if op == OpcodeTypedSelect {
-				if err := enabledFeatures.Require(FeatureReferenceTypes); err != nil {
+				if err := enabledFeatures.RequireEnabled(api.CoreFeatureReferenceTypes); err != nil {
 					return fmt.Errorf("%s is invalid as %w", InstructionName(op), err)
 				}
 				pc++
@@ -1844,11 +1844,11 @@ type controlBlock struct {
 
 // DecodeBlockType decodes the type index from a positive 33-bit signed integer. Negative numbers indicate up to one
 // WebAssembly 1.0 (20191205) compatible result type. Positive numbers are decoded when `enabledFeatures` include
-// FeatureMultiValue and include an index in the Module.TypeSection.
+// CoreFeatureMultiValue and include an index in the Module.TypeSection.
 //
 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-blocktype
 // See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
-func DecodeBlockType(types []*FunctionType, r *bytes.Reader, enabledFeatures Features) (*FunctionType, uint64, error) {
+func DecodeBlockType(types []*FunctionType, r *bytes.Reader, enabledFeatures api.CoreFeatures) (*FunctionType, uint64, error) {
 	raw, num, err := leb128.DecodeInt33AsInt64(r)
 	if err != nil {
 		return nil, 0, fmt.Errorf("decode int33: %w", err)
@@ -1873,7 +1873,7 @@ func DecodeBlockType(types []*FunctionType, r *bytes.Reader, enabledFeatures Fea
 	case -17: // 0x6f in original byte = externref
 		ret = &FunctionType{Results: []ValueType{ValueTypeExternref}, ResultNumInUint64: 1}
 	default:
-		if err = enabledFeatures.Require(FeatureMultiValue); err != nil {
+		if err = enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
 			return nil, num, fmt.Errorf("block with function type return invalid as %v", err)
 		}
 		if raw < 0 || (raw >= int64(len(types))) {
diff --git a/internal/wasm/host.go b/internal/wasm/host.go
index a3e499ff..00307bfc 100644
--- a/internal/wasm/host.go
+++ b/internal/wasm/host.go
@@ -6,6 +6,7 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/wasmdebug"
 )
 
@@ -92,7 +93,7 @@ func NewHostModule(
 	funcToNames map[string][]string,
 	nameToMemory map[string]*Memory,
 	nameToGlobal map[string]*Global,
-	enabledFeatures Features,
+	enabledFeatures api.CoreFeatures,
 ) (m *Module, err error) {
 	if moduleName != "" {
 		m = &Module{NameSection: &NameSection{ModuleName: moduleName}}
@@ -162,7 +163,7 @@ func addFuncs(
 	m *Module,
 	nameToGoFunc map[string]interface{},
 	funcToNames map[string][]string,
-	enabledFeatures Features,
+	enabledFeatures api.CoreFeatures,
 ) (err error) {
 	if m.NameSection == nil {
 		m.NameSection = &NameSection{}
@@ -307,10 +308,10 @@ func addGlobals(m *Module, globals map[string]*Global) error {
 	return nil
 }
 
-func (m *Module) maybeAddType(params, results []ValueType, enabledFeatures Features) (Index, error) {
+func (m *Module) maybeAddType(params, results []ValueType, enabledFeatures api.CoreFeatures) (Index, error) {
 	if len(results) > 1 {
 		// Guard >1.0 feature multi-value
-		if err := enabledFeatures.Require(FeatureMultiValue); err != nil {
+		if err := enabledFeatures.RequireEnabled(api.CoreFeatureMultiValue); err != nil {
 			return 0, fmt.Errorf("multiple result types invalid as %v", err)
 		}
 	}
diff --git a/internal/wasm/instruction.go b/internal/wasm/instruction.go
index e92fc706..ba65e9a2 100644
--- a/internal/wasm/instruction.go
+++ b/internal/wasm/instruction.go
@@ -24,13 +24,13 @@ const (
 	OpcodeEnd Opcode = 0x0b
 
 	// OpcodeBr is a stack-polymorphic opcode that performs an unconditional branch. How the stack is modified depends
-	// on whether the "br" is enclosed by a loop, and if FeatureMultiValue is enabled.
+	// on whether the "br" is enclosed by a loop, and if CoreFeatureMultiValue is enabled.
 	//
 	// Here are the rules in pseudocode about how the stack is modified based on the "br" operand L (label):
 	//	if L is loop: append(L.originalStackWithoutInputs, N-values popped from the stack) where N == L.inputs
 	//	else: append(L.originalStackWithoutInputs, N-values popped from the stack) where N == L.results
 	//
-	// In WebAssembly 1.0 (20191205), N can be zero or one. When FeatureMultiValue is enabled, N can be more than one,
+	// In WebAssembly 1.0 (20191205), N can be zero or one. When CoreFeatureMultiValue is enabled, N can be more than one,
 	// depending on the type use of the label L.
 	//
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-hrefsyntax-instr-controlmathsfbrl
@@ -57,7 +57,7 @@ const (
 	OpcodeGlobalGet Opcode = 0x23
 	OpcodeGlobalSet Opcode = 0x24
 
-	// Below are toggled with FeatureReferenceTypes
+	// Below are toggled with CoreFeatureReferenceTypes
 
 	OpcodeTableGet Opcode = 0x25
 	OpcodeTableSet Opcode = 0x26
@@ -236,50 +236,50 @@ const (
 	OpcodeF64ReinterpretI64 Opcode = 0xbf
 
 	// OpcodeRefNull pushes a null reference value whose type is specified by immediate to this opcode.
-	// This is defined in the reference-types proposal, but necessary for FeatureBulkMemoryOperations as well.
+	// This is defined in the reference-types proposal, but necessary for CoreFeatureBulkMemoryOperations as well.
 	//
 	// Currently only supported in the constant expression in element segments.
 	OpcodeRefNull = 0xd0
 	// OpcodeRefIsNull pops a reference value, and pushes 1 if it is null, 0 otherwise.
-	// This is defined in the reference-types proposal, but necessary for FeatureBulkMemoryOperations as well.
+	// This is defined in the reference-types proposal, but necessary for CoreFeatureBulkMemoryOperations as well.
 	//
 	// Currently not supported.
 	OpcodeRefIsNull = 0xd1
 	// OpcodeRefFunc pushes a funcref value whose index equals the immediate to this opcode.
-	// This is defined in the reference-types proposal, but necessary for FeatureBulkMemoryOperations as well.
+	// This is defined in the reference-types proposal, but necessary for CoreFeatureBulkMemoryOperations as well.
 	//
 	// Currently, this is only supported in the constant expression in element segments.
 	OpcodeRefFunc = 0xd2
 
-	// Below are toggled with FeatureSignExtensionOps
+	// Below are toggled with CoreFeatureSignExtensionOps
 
 	// OpcodeI32Extend8S extends a signed 8-bit integer to a 32-bit integer.
-	// Note: This is dependent on the flag FeatureSignExtensionOps
+	// Note: This is dependent on the flag CoreFeatureSignExtensionOps
 	OpcodeI32Extend8S Opcode = 0xc0
 
 	// OpcodeI32Extend16S extends a signed 16-bit integer to a 32-bit integer.
-	// Note: This is dependent on the flag FeatureSignExtensionOps
+	// Note: This is dependent on the flag CoreFeatureSignExtensionOps
 	OpcodeI32Extend16S Opcode = 0xc1
 
 	// OpcodeI64Extend8S extends a signed 8-bit integer to a 64-bit integer.
-	// Note: This is dependent on the flag FeatureSignExtensionOps
+	// Note: This is dependent on the flag CoreFeatureSignExtensionOps
 	OpcodeI64Extend8S Opcode = 0xc2
 
 	// OpcodeI64Extend16S extends a signed 16-bit integer to a 64-bit integer.
-	// Note: This is dependent on the flag FeatureSignExtensionOps
+	// Note: This is dependent on the flag CoreFeatureSignExtensionOps
 	OpcodeI64Extend16S Opcode = 0xc3
 
 	// OpcodeI64Extend32S extends a signed 32-bit integer to a 64-bit integer.
-	// Note: This is dependent on the flag FeatureSignExtensionOps
+	// Note: This is dependent on the flag CoreFeatureSignExtensionOps
 	OpcodeI64Extend32S Opcode = 0xc4
 
 	// OpcodeMiscPrefix is the prefix of various multi-byte opcodes.
-	// Introduced in FeatureNonTrappingFloatToIntConversion, but used in other
-	// features, such as FeatureBulkMemoryOperations.
+	// Introduced in CoreFeatureNonTrappingFloatToIntConversion, but used in other
+	// features, such as CoreFeatureBulkMemoryOperations.
 	OpcodeMiscPrefix Opcode = 0xfc
 
 	// OpcodeVecPrefix is the prefix of all vector isntructions introduced in
-	// FeatureSIMD.
+	// CoreFeatureSIMD.
 	OpcodeVecPrefix Opcode = 0xfd
 )
 
@@ -288,7 +288,7 @@ const (
 type OpcodeMisc = byte
 
 const (
-	// Below are toggled with FeatureNonTrappingFloatToIntConversion.
+	// Below are toggled with CoreFeatureNonTrappingFloatToIntConversion.
 	// https://github.com/WebAssembly/spec/blob/ce4b6c4d47eb06098cc7ab2e81f24748da822f20/proposals/nontrapping-float-to-int-conversion/Overview.md
 
 	OpcodeMiscI32TruncSatF32S OpcodeMisc = 0x00
@@ -300,7 +300,7 @@ const (
 	OpcodeMiscI64TruncSatF64S OpcodeMisc = 0x06
 	OpcodeMiscI64TruncSatF64U OpcodeMisc = 0x07
 
-	// Below are toggled with FeatureBulkMemoryOperations.
+	// Below are toggled with CoreFeatureBulkMemoryOperations.
 	// Opcodes are those new in document/core/appendix/index-instructions.rst (the commit that merged the feature).
 	// See https://github.com/WebAssembly/spec/commit/7fa2f20a6df4cf1c114582c8cb60f5bfcdbf1be1
 	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
@@ -313,7 +313,7 @@ const (
 	OpcodeMiscElemDrop   OpcodeMisc = 0x0d
 	OpcodeMiscTableCopy  OpcodeMisc = 0x0e
 
-	// Below are toggled with FeatureReferenceTypes
+	// Below are toggled with CoreFeatureReferenceTypes
 
 	OpcodeMiscTableGrow OpcodeMisc = 0x0f
 	OpcodeMiscTableSize OpcodeMisc = 0x10
@@ -323,7 +323,7 @@ const (
 // OpcodeVec represents an opcode of a vector instructions which has
 // multi-byte encoding and is prefixed by OpcodeMiscPrefix.
 //
-// These opcodes are toggled with FeatureSIMD.
+// These opcodes are toggled with CoreFeatureSIMD.
 type OpcodeVec = byte
 
 const (
@@ -805,7 +805,7 @@ const (
 	OpcodeTableGetName = "table.get"
 	OpcodeTableSetName = "table.set"
 
-	// Below are toggled with FeatureSignExtensionOps
+	// Below are toggled with CoreFeatureSignExtensionOps
 
 	OpcodeI32Extend8SName  = "i32.extend8_s"
 	OpcodeI32Extend16SName = "i32.extend16_s"
@@ -999,7 +999,7 @@ var instructionNames = [256]string{
 	OpcodeTableGet: OpcodeTableGetName,
 	OpcodeTableSet: OpcodeTableSetName,
 
-	// Below are toggled with FeatureSignExtensionOps
+	// Below are toggled with CoreFeatureSignExtensionOps
 
 	OpcodeI32Extend8S:  OpcodeI32Extend8SName,
 	OpcodeI32Extend16S: OpcodeI32Extend16SName,
diff --git a/internal/wasm/module.go b/internal/wasm/module.go
index 00a0185c..dea329e4 100644
--- a/internal/wasm/module.go
+++ b/internal/wasm/module.go
@@ -10,7 +10,7 @@ import (
 	"strings"
 
 	"github.com/tetratelabs/wazero/api"
-	experimental "github.com/tetratelabs/wazero/experimental"
+	"github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/internal/ieee754"
 	"github.com/tetratelabs/wazero/internal/leb128"
 )
@@ -24,7 +24,7 @@ import (
 // See binary.DecodeModule and text.DecodeModule
 type DecodeModule func(
 	wasm []byte,
-	enabledFeatures Features,
+	enabledFeatures api.CoreFeatures,
 	memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32),
 ) (result *Module, err error)
 
@@ -43,7 +43,7 @@ type Module struct {
 	// TypeSection contains the unique FunctionType of functions imported or defined in this module.
 	//
 	// Note: Currently, there is no type ambiguity in the index as WebAssembly 1.0 only defines function type.
-	// In the future, other types may be introduced to support Features such as module linking.
+	// In the future, other types may be introduced to support CoreFeatures such as module linking.
 	//
 	// Note: In the Binary Format, this is SectionIDType.
 	//
@@ -167,7 +167,7 @@ type Module struct {
 
 	// DataCountSection is the optional section and holds the number of data segments in the data section.
 	//
-	// Note: This may exist in WebAssembly 2.0 or WebAssembly 1.0 with FeatureBulkMemoryOperations.
+	// Note: This may exist in WebAssembly 2.0 or WebAssembly 1.0 with CoreFeatureBulkMemoryOperations.
 	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section
 	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
 	DataCountSection *uint32
@@ -226,7 +226,7 @@ func (m *Module) TypeOfFunction(funcIdx Index) *FunctionType {
 	return m.TypeSection[typeIdx]
 }
 
-func (m *Module) Validate(enabledFeatures Features) error {
+func (m *Module) Validate(enabledFeatures api.CoreFeatures) error {
 	for _, tp := range m.TypeSection {
 		tp.CacheNumInUint64()
 	}
@@ -304,7 +304,7 @@ func (m *Module) validateGlobals(globals []*GlobalType, numFuncts, maxGlobals ui
 	return nil
 }
 
-func (m *Module) validateFunctions(enabledFeatures Features, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table, maximumFunctionIndex uint32) error {
+func (m *Module) validateFunctions(enabledFeatures api.CoreFeatures, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table, maximumFunctionIndex uint32) error {
 	if uint32(len(functions)) > maximumFunctionIndex {
 		return fmt.Errorf("too many functions in a store")
 	}
@@ -406,7 +406,7 @@ func (m *Module) funcDesc(sectionID SectionID, sectionIndex Index) string {
 	return fmt.Sprintf("%s[%d] export[%s]", sectionIDName, sectionIndex, strings.Join(exportNames, ","))
 }
 
-func (m *Module) validateMemory(memory *Memory, globals []*GlobalType, enabledFeatures Features) error {
+func (m *Module) validateMemory(memory *Memory, globals []*GlobalType, _ api.CoreFeatures) error {
 	var activeElementCount int
 	for _, sec := range m.DataSection {
 		if !sec.IsPassive() {
@@ -427,14 +427,14 @@ func (m *Module) validateMemory(memory *Memory, globals []*GlobalType, enabledFe
 	return nil
 }
 
-func (m *Module) validateImports(enabledFeatures Features) error {
+func (m *Module) validateImports(enabledFeatures api.CoreFeatures) error {
 	for _, i := range m.ImportSection {
 		switch i.Type {
 		case ExternTypeGlobal:
 			if !i.DescGlobal.Mutable {
 				continue
 			}
-			if err := enabledFeatures.Require(FeatureMutableGlobal); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureMutableGlobal); err != nil {
 				return fmt.Errorf("invalid import[%q.%q] global: %w", i.Module, i.Name, err)
 			}
 		}
@@ -442,7 +442,7 @@ func (m *Module) validateImports(enabledFeatures Features) error {
 	return nil
 }
 
-func (m *Module) validateExports(enabledFeatures Features, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table) error {
+func (m *Module) validateExports(enabledFeatures api.CoreFeatures, functions []Index, globals []*GlobalType, memory *Memory, tables []*Table) error {
 	for _, exp := range m.ExportSection {
 		index := exp.Index
 		switch exp.Type {
@@ -457,7 +457,7 @@ func (m *Module) validateExports(enabledFeatures Features, functions []Index, gl
 			if !globals[index].Mutable {
 				continue
 			}
-			if err := enabledFeatures.Require(FeatureMutableGlobal); err != nil {
+			if err := enabledFeatures.RequireEnabled(api.CoreFeatureMutableGlobal); err != nil {
 				return fmt.Errorf("invalid export[%q] global[%d]: %w", exp.Name, index, err)
 			}
 		case ExternTypeMemory:
@@ -960,7 +960,7 @@ const (
 	SectionIDCode
 	SectionIDData
 
-	// SectionIDDataCount may exist in WebAssembly 2.0 or WebAssembly 1.0 with FeatureBulkMemoryOperations enabled.
+	// SectionIDDataCount may exist in WebAssembly 2.0 or WebAssembly 1.0 with CoreFeatureBulkMemoryOperations enabled.
 	//
 	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section
 	// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
diff --git a/internal/wasm/store.go b/internal/wasm/store.go
index 3f596a57..41c406d2 100644
--- a/internal/wasm/store.go
+++ b/internal/wasm/store.go
@@ -31,7 +31,7 @@ type (
 	// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#store%E2%91%A0
 	Store struct {
 		// EnabledFeatures are read-only to allow optimizations.
-		EnabledFeatures Features
+		EnabledFeatures api.CoreFeatures
 
 		// Engine is a global context for a Store which is in responsible for compilation and execution of Wasm modules.
 		Engine Engine
@@ -268,7 +268,7 @@ func (m *ModuleInstance) getExport(name string, et ExternType) (*ExportInstance,
 	return exp, nil
 }
 
-func NewStore(enabledFeatures Features, engine Engine) (*Store, *Namespace) {
+func NewStore(enabledFeatures api.CoreFeatures, engine Engine) (*Store, *Namespace) {
 	ns := newNamespace()
 	return &Store{
 		EnabledFeatures:  enabledFeatures,
@@ -354,7 +354,7 @@ func (s *Store) instantiate(
 
 	tables, tableInit, err := module.buildTables(importedTables, importedGlobals,
 		// As of reference-types proposal, boundary check must be done after instantiation.
-		s.EnabledFeatures.Get(FeatureReferenceTypes))
+		s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes))
 	if err != nil {
 		return nil, err
 	}
@@ -369,7 +369,7 @@ func (s *Store) instantiate(
 	// As of reference types proposal, data segment validation must happen after instantiation,
 	// and the side effect must persist even if there's out of bounds error after instantiation.
 	// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L395-L405
-	if !s.EnabledFeatures.Get(FeatureReferenceTypes) {
+	if !s.EnabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
 		if err = m.validateData(module.DataSection); err != nil {
 			return nil, err
 		}
diff --git a/internal/wasm/table.go b/internal/wasm/table.go
index bc42bcb7..7d97d79c 100644
--- a/internal/wasm/table.go
+++ b/internal/wasm/table.go
@@ -7,6 +7,7 @@ import (
 	"math"
 	"sync"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 )
 
@@ -142,7 +143,7 @@ type validatedActiveElementSegment struct {
 
 // validateTable ensures any ElementSegment is valid. This caches results via Module.validatedActiveElementSegments.
 // Note: limitsType are validated by decoders, so not re-validated here.
-func (m *Module) validateTable(enabledFeatures Features, tables []*Table, maximumTableIndex uint32) ([]*validatedActiveElementSegment, error) {
+func (m *Module) validateTable(enabledFeatures api.CoreFeatures, tables []*Table, maximumTableIndex uint32) ([]*validatedActiveElementSegment, error) {
 	if len(tables) > int(maximumTableIndex) {
 		return nil, fmt.Errorf("too many tables in a module: %d given with limit %d", len(tables), maximumTableIndex)
 	}
@@ -209,7 +210,7 @@ func (m *Module) validateTable(enabledFeatures Features, tables []*Table, maximu
 				// Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L117 we must pass if imported
 				// table has set its min=0. Per https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/elem.wast#L142, we
 				// have to do fail if module-defined min=0.
-				if !enabledFeatures.Get(FeatureReferenceTypes) && elem.TableIndex >= importedTableCount {
+				if !enabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) && elem.TableIndex >= importedTableCount {
 					if err = checkSegmentBounds(t.Min, uint64(initCount)+uint64(offset), idx); err != nil {
 						return nil, err
 					}
diff --git a/internal/wazeroir/compiler.go b/internal/wazeroir/compiler.go
index 2ce982eb..607c977e 100644
--- a/internal/wazeroir/compiler.go
+++ b/internal/wazeroir/compiler.go
@@ -10,6 +10,7 @@ import (
 	"reflect"
 	"strings"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/buildoptions"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/wasm"
@@ -155,7 +156,7 @@ func (c *compiler) initializeStack() {
 }
 
 type compiler struct {
-	enabledFeatures            wasm.Features
+	enabledFeatures            api.CoreFeatures
 	callFrameStackSizeInUint64 int
 	stack                      []UnsignedType
 	currentID                  uint32
@@ -250,7 +251,7 @@ type CompilationResult struct {
 	HasElementInstances bool
 }
 
-func CompileFunctions(_ context.Context, enabledFeatures wasm.Features, callFrameStackSizeInUint64 int, module *wasm.Module) ([]*CompilationResult, error) {
+func CompileFunctions(_ context.Context, enabledFeatures api.CoreFeatures, callFrameStackSizeInUint64 int, module *wasm.Module) ([]*CompilationResult, error) {
 	functions, globals, mem, tables, err := module.AllDeclarations()
 	if err != nil {
 		return nil, err
@@ -308,7 +309,7 @@ func CompileFunctions(_ context.Context, enabledFeatures wasm.Features, callFram
 // Compile lowers given function instance into wazeroir operations
 // so that the resulting operations can be consumed by the interpreter
 // or the Compiler compilation engine.
-func compile(enabledFeatures wasm.Features,
+func compile(enabledFeatures api.CoreFeatures,
 	callFrameStackSizeInUint64 int,
 	sig *wasm.FunctionType,
 	body []byte,
diff --git a/runtime.go b/runtime.go
index 7c345e2e..3de949ea 100644
--- a/runtime.go
+++ b/runtime.go
@@ -141,7 +141,7 @@ func NewRuntimeWithConfig(ctx context.Context, rConfig RuntimeConfig) Runtime {
 type runtime struct {
 	store           *wasm.Store
 	ns              *namespace
-	enabledFeatures wasm.Features
+	enabledFeatures api.CoreFeatures
 	isInterpreter   bool
 	compiledModules []*compiledModule
 }
diff --git a/site/content/languages/go.md b/site/content/languages/go.md
index 7cf0d284..224a06c0 100644
--- a/site/content/languages/go.md
+++ b/site/content/languages/go.md
@@ -34,7 +34,7 @@ Due to lack of adoption, support and relatively high implementation overhead,
 most choose [TinyGo]({{< relref "/tinygo.md" >}}) to compile source code, even if it supports less
 features.
 
-## WebAssembly Features
+## WebAssembly CoreFeatures
 
 `GOARCH=wasm GOOS=js` uses instructions in [WebAssembly Core Specification 1.0]
 [15] unless `GOWASM` includes features added afterwards.
diff --git a/site/content/specs.md b/site/content/specs.md
index 4cba286b..3f8fb9d4 100644
--- a/site/content/specs.md
+++ b/site/content/specs.md
@@ -35,10 +35,11 @@ as well as how to classify a request for a feature we don't yet support.
 wazero conforms with tests defined alongside WebAssembly Core
 Specification [1.0][1] and [2.0][14].
 
-By default, the runtime configuration only enables WebAssembly 1.0 features, but
-you can opt in via the below configuration:
+By default, the runtime configuration enables features in WebAssembly Core
+Specification, despite it not yet being a Web Standard (REC). You can select
+version 1.0 like so:
 ```go
-rConfig = wazero.NewRuntimeConfig().WithWasmCore2()
+rConfig = wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV1)
 ```
 
 One current limitation of wazero is that it doesn't implement the Text
@@ -46,13 +47,13 @@ Format, e.g. compiling `.wat` files. Users can work around this using tools such
 compile the text format into the binary format. In practice, the text format is
 too low level for most users, so delays here have limited impact.
 
-#### Post 2.0 Features
-Features regardless of W3C release are inventoried in the [Proposals][10].
+#### Post 2.0 CoreFeatures
+CoreFeatures regardless of W3C release are inventoried in the [Proposals][10].
 repository. wazero implements [Finished Proposals][11] based on user demand,
 using [wazero.RuntimeConfig][7] feature flags. As of mid 2022, all finished proposals
 are included in [2.0][14] Working Draft.
 
-Features not yet assigned to a W3C release are not reliable. Encourage the
+CoreFeatures not yet assigned to a W3C release are not reliable. Encourage the
 [WebAssembly community][12] to formalize features you rely on, so that they
 become assigned to a release, and reach the W3C recommendation (REC) phase.
 


Test patch:
diff --git a/api/features_test.go b/api/features_test.go
new file mode 100644
index 00000000..673046d2
--- /dev/null
+++ b/api/features_test.go
@@ -0,0 +1,103 @@
+package api
+
+import (
+	"testing"
+
+	"github.com/tetratelabs/wazero/internal/testing/require"
+)
+
+// TestCoreFeatures_ZeroIsInvalid reminds maintainers that a bitset cannot use zero as a flag!
+// This is why we start iota with 1.
+func TestCoreFeatures_ZeroIsInvalid(t *testing.T) {
+	f := CoreFeatures(0)
+	f = f.SetEnabled(0, true)
+	require.False(t, f.IsEnabled(0))
+}
+
+// TestCoreFeatures tests the bitset works as expected
+func TestCoreFeatures(t *testing.T) {
+	tests := []struct {
+		name    string
+		feature CoreFeatures
+	}{
+		{
+			name:    "one is the smallest flag",
+			feature: 1,
+		},
+		{
+			name:    "63 is the largest feature flag", // because uint64
+			feature: 1 << 63,
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			f := CoreFeatures(0)
+
+			// Defaults to false
+			require.False(t, f.IsEnabled(tc.feature))
+
+			// Set true makes it true
+			f = f.SetEnabled(tc.feature, true)
+			require.True(t, f.IsEnabled(tc.feature))
+
+			// Set false makes it false again
+			f = f.SetEnabled(tc.feature, false)
+			require.False(t, f.IsEnabled(tc.feature))
+		})
+	}
+}
+
+func TestCoreFeatures_String(t *testing.T) {
+	tests := []struct {
+		name     string
+		feature  CoreFeatures
+		expected string
+	}{
+		{name: "none", feature: 0, expected: ""},
+		{name: "mutable-global", feature: CoreFeatureMutableGlobal, expected: "mutable-global"},
+		{name: "sign-extension-ops", feature: CoreFeatureSignExtensionOps, expected: "sign-extension-ops"},
+		{name: "multi-value", feature: CoreFeatureMultiValue, expected: "multi-value"},
+		{name: "simd", feature: CoreFeatureSIMD, expected: "simd"},
+		{name: "features", feature: CoreFeatureMutableGlobal | CoreFeatureMultiValue, expected: "multi-value|mutable-global"},
+		{name: "undefined", feature: 1 << 63, expected: ""},
+		{name: "2.0", feature: CoreFeaturesV2,
+			expected: "bulk-memory-operations|multi-value|mutable-global|" +
+				"nontrapping-float-to-int-conversion|reference-types|sign-extension-ops|simd"},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			require.Equal(t, tc.expected, tc.feature.String())
+		})
+	}
+}
+
+func TestCoreFeatures_Require(t *testing.T) {
+	tests := []struct {
+		name        string
+		feature     CoreFeatures
+		expectedErr string
+	}{
+		{name: "none", feature: 0, expectedErr: "feature \"mutable-global\" is disabled"},
+		{name: "mutable-global", feature: CoreFeatureMutableGlobal},
+		{name: "sign-extension-ops", feature: CoreFeatureSignExtensionOps, expectedErr: "feature \"mutable-global\" is disabled"},
+		{name: "multi-value", feature: CoreFeatureMultiValue, expectedErr: "feature \"mutable-global\" is disabled"},
+		{name: "undefined", feature: 1 << 63, expectedErr: "feature \"mutable-global\" is disabled"},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+		t.Run(tc.name, func(t *testing.T) {
+			err := tc.feature.RequireEnabled(CoreFeatureMutableGlobal)
+			if tc.expectedErr != "" {
+				require.EqualError(t, err, tc.expectedErr)
+			} else {
+				require.NoError(t, err)
+			}
+		})
+	}
+}
diff --git a/config_test.go b/config_test.go
index 729450fe..78db1cbf 100644
--- a/config_test.go
+++ b/config_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing/fstest"
 
+	"github.com/tetratelabs/wazero/api"
 	internalsys "github.com/tetratelabs/wazero/internal/sys"
 	testfs "github.com/tetratelabs/wazero/internal/testing/fs"
 	"github.com/tetratelabs/wazero/internal/testing/require"
@@ -24,84 +25,12 @@ func TestRuntimeConfig(t *testing.T) {
 		expected RuntimeConfig
 	}{
 		{
-			name: "bulk-memory-operations",
+			name: "features",
 			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureBulkMemoryOperations(true)
+				return c.WithCoreFeatures(api.CoreFeaturesV1)
 			},
 			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureBulkMemoryOperations | wasm.FeatureReferenceTypes,
-			},
-		},
-		{
-			name: "multi-value",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureMultiValue(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureMultiValue,
-			},
-		},
-		{
-			name: "mutable-global",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureMutableGlobal(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureMutableGlobal,
-			},
-		},
-		{
-			name: "nontrapping-float-to-int-conversion",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureNonTrappingFloatToIntConversion(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureNonTrappingFloatToIntConversion,
-			},
-		},
-		{
-			name: "sign-extension-ops",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureSignExtensionOps(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureSignExtensionOps,
-			},
-		},
-		{
-			name: "REC-wasm-core-1-20191205",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureSignExtensionOps(true).WithWasmCore1()
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.Features20191205,
-			},
-		},
-		{
-			name: "WD-wasm-core-2-20220419",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureMutableGlobal(false).WithWasmCore2()
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.Features20220419,
-			},
-		},
-		{
-			name: "reference-types",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureReferenceTypes(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureBulkMemoryOperations | wasm.FeatureReferenceTypes,
-			},
-		},
-		{
-			name: "simd",
-			with: func(c RuntimeConfig) RuntimeConfig {
-				return c.WithFeatureSIMD(true)
-			},
-			expected: &runtimeConfig{
-				enabledFeatures: wasm.FeatureSIMD,
+				enabledFeatures: api.CoreFeaturesV1,
 			},
 		},
 	}
@@ -118,77 +47,6 @@ func TestRuntimeConfig(t *testing.T) {
 	}
 }
 
-func TestRuntimeConfig_FeatureToggle(t *testing.T) {
-	tests := []struct {
-		name          string
-		feature       wasm.Features
-		expectDefault bool
-		setFeature    func(RuntimeConfig, bool) RuntimeConfig
-	}{
-		{
-			name:          "bulk-memory-operations",
-			feature:       wasm.FeatureBulkMemoryOperations,
-			expectDefault: false,
-			setFeature: func(c RuntimeConfig, v bool) RuntimeConfig {
-				return c.WithFeatureBulkMemoryOperations(v)
-			},
-		},
-		{
-			name:          "multi-value",
-			feature:       wasm.FeatureMultiValue,
-			expectDefault: false,
-			setFeature: func(c RuntimeConfig, v bool) RuntimeConfig {
-				return c.WithFeatureMultiValue(v)
-			},
-		},
-		{
-			name:          "mutable-global",
-			feature:       wasm.FeatureMutableGlobal,
-			expectDefault: true,
-			setFeature: func(c RuntimeConfig, v bool) RuntimeConfig {
-				return c.WithFeatureMutableGlobal(v)
-			},
-		},
-		{
-			name:          "nontrapping-float-to-int-conversion",
-			feature:       wasm.FeatureNonTrappingFloatToIntConversion,
-			expectDefault: false,
-			setFeature: func(c RuntimeConfig, v bool) RuntimeConfig {
-				return c.WithFeatureNonTrappingFloatToIntConversion(v)
-			},
-		},
-		{
-			name:          "sign-extension-ops",
-			feature:       wasm.FeatureSignExtensionOps,
-			expectDefault: false,
-			setFeature: func(c RuntimeConfig, v bool) RuntimeConfig {
-				return c.WithFeatureSignExtensionOps(v)
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		tc := tt
-
-		t.Run(tc.name, func(t *testing.T) {
-			c := NewRuntimeConfig().(*runtimeConfig)
-			require.Equal(t, tc.expectDefault, c.enabledFeatures.Get(tc.feature))
-
-			// Set to false even if it was initially false.
-			c = tc.setFeature(c, false).(*runtimeConfig)
-			require.False(t, c.enabledFeatures.Get(tc.feature))
-
-			// Set true makes it true
-			c = tc.setFeature(c, true).(*runtimeConfig)
-			require.True(t, c.enabledFeatures.Get(tc.feature))
-
-			// Set false makes it false again
-			c = tc.setFeature(c, false).(*runtimeConfig)
-			require.False(t, c.enabledFeatures.Get(tc.feature))
-		})
-	}
-}
-
 func TestCompileConfig(t *testing.T) {
 	mp := func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) {
 		return 0, 1, 1
diff --git a/example_test.go b/example_test.go
index 0af023e8..ea9a0b5a 100644
--- a/example_test.go
+++ b/example_test.go
@@ -30,9 +30,7 @@ func Example() {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of any version of TinyGo, including 0.24+.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	// Instantiate WASI, which implements host functions needed for TinyGo to
diff --git a/imports/go/example/stars_test.go b/imports/go/example/stars_test.go
index 24e97dd4..66b3588f 100644
--- a/imports/go/example/stars_test.go
+++ b/imports/go/example/stars_test.go
@@ -64,9 +64,7 @@ func Benchmark_main(b *testing.B) {
 	ctx := context.Background()
 
 	// Create a new WebAssembly Runtime.
-	r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
-		// WebAssembly 2.0 allows use of gojs.
-		WithWasmCore2())
+	r := wazero.NewRuntime(ctx)
 	defer r.Close(ctx) // This closes everything this Runtime created.
 
 	bin, err := os.ReadFile(path.Join("stars", "main.wasm"))
diff --git a/internal/engine/compiler/engine_test.go b/internal/engine/compiler/engine_test.go
index d10c8c22..f02c73b9 100644
--- a/internal/engine/compiler/engine_test.go
+++ b/internal/engine/compiler/engine_test.go
@@ -36,7 +36,7 @@ func (e *engineTester) ListenerFactory() experimental.FunctionListenerFactory {
 }
 
 // NewEngine implements the same method as documented on enginetest.EngineTester.
-func (e *engineTester) NewEngine(enabledFeatures wasm.Features) wasm.Engine {
+func (e *engineTester) NewEngine(enabledFeatures api.CoreFeatures) wasm.Engine {
 	return newEngine(context.Background(), enabledFeatures)
 }
 
@@ -116,7 +116,7 @@ func (f fakeFinalizer) setFinalizer(obj interface{}, finalizer interface{}) {
 
 func TestCompiler_CompileModule(t *testing.T) {
 	t.Run("ok", func(t *testing.T) {
-		e := et.NewEngine(wasm.Features20191205).(*engine)
+		e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 		ff := fakeFinalizer{}
 		e.setFinalizer = ff.setFinalizer
 
@@ -162,7 +162,7 @@ func TestCompiler_CompileModule(t *testing.T) {
 		}
 		errModule.BuildFunctionDefinitions()
 
-		e := et.NewEngine(wasm.Features20191205).(*engine)
+		e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 		err := e.CompileModule(testCtx, errModule)
 		require.EqualError(t, err, "failed to lower func[.$2] to wazeroir: handling instruction: apply stack failed for call: reading immediates: EOF")
 
@@ -188,7 +188,7 @@ func TestCompiler_Releasecode_Panic(t *testing.T) {
 // allows us to safely access to their data region from native code.
 // See comments on initialStackSize and initialCallFrameStackSize.
 func TestCompiler_SliceAllocatedOnHeap(t *testing.T) {
-	enabledFeatures := wasm.Features20191205
+	enabledFeatures := api.CoreFeaturesV1
 	e := newEngine(context.Background(), enabledFeatures)
 	s, ns := wasm.NewStore(enabledFeatures, e)
 
diff --git a/internal/engine/interpreter/interpreter_test.go b/internal/engine/interpreter/interpreter_test.go
index 0fc8e457..fab0de72 100644
--- a/internal/engine/interpreter/interpreter_test.go
+++ b/internal/engine/interpreter/interpreter_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 	"unsafe"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/experimental"
 	"github.com/tetratelabs/wazero/experimental/logging"
 	"github.com/tetratelabs/wazero/internal/testing/enginetest"
@@ -82,7 +83,7 @@ func (e engineTester) ListenerFactory() experimental.FunctionListenerFactory {
 }
 
 // NewEngine implements enginetest.EngineTester NewEngine.
-func (e engineTester) NewEngine(enabledFeatures wasm.Features) wasm.Engine {
+func (e engineTester) NewEngine(enabledFeatures api.CoreFeatures) wasm.Engine {
 	return NewEngine(context.Background(), enabledFeatures)
 }
 
@@ -506,7 +507,7 @@ func TestInterpreter_CallEngine_callNativeFunc_signExtend(t *testing.T) {
 
 func TestInterpreter_Compile(t *testing.T) {
 	t.Run("uncompiled", func(t *testing.T) {
-		e := et.NewEngine(wasm.Features20191205).(*engine)
+		e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 		_, err := e.NewModuleEngine("foo",
 			&wasm.Module{},
 			nil, // imports
@@ -517,7 +518,7 @@ func TestInterpreter_Compile(t *testing.T) {
 		require.EqualError(t, err, "source module for foo must be compiled before instantiation")
 	})
 	t.Run("fail", func(t *testing.T) {
-		e := et.NewEngine(wasm.Features20191205).(*engine)
+		e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 
 		errModule := &wasm.Module{
 			TypeSection:     []*wasm.FunctionType{{}},
@@ -539,7 +540,7 @@ func TestInterpreter_Compile(t *testing.T) {
 		require.False(t, ok)
 	})
 	t.Run("ok", func(t *testing.T) {
-		e := et.NewEngine(wasm.Features20191205).(*engine)
+		e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 
 		okModule := &wasm.Module{
 			TypeSection:     []*wasm.FunctionType{{}},
@@ -565,7 +566,7 @@ func TestInterpreter_Compile(t *testing.T) {
 }
 
 func TestEngine_CachedcodesPerModule(t *testing.T) {
-	e := et.NewEngine(wasm.Features20191205).(*engine)
+	e := et.NewEngine(api.CoreFeaturesV1).(*engine)
 	exp := []*code{
 		{body: []*interpreterOp{}},
 		{body: []*interpreterOp{}},
diff --git a/internal/gojs/compiler_test.go b/internal/gojs/compiler_test.go
index 6b3f3a17..fc561e6d 100644
--- a/internal/gojs/compiler_test.go
+++ b/internal/gojs/compiler_test.go
@@ -24,7 +24,7 @@ import (
 func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) {
 	var stdoutBuf, stderrBuf bytes.Buffer
 
-	r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithWasmCore2())
+	r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig())
 	defer r.Close(ctx)
 
 	compiled, compileErr := r.CompileModule(ctx, testBin, wazero.NewCompileConfig())
@@ -83,7 +83,7 @@ func TestMain(m *testing.M) {
 
 	// Seed wazero's compilation cache to see any error up-front and to prevent
 	// one test from a cache-miss performance penalty.
-	rt := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithWasmCore2())
+	rt := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig())
 	defer rt.Close(testCtx)
 	_, err = rt.CompileModule(testCtx, testBin, wazero.NewCompileConfig())
 	if err != nil {
diff --git a/internal/gojs/spfunc/spfunc_test.go b/internal/gojs/spfunc/spfunc_test.go
index b3954b41..e2b76ee3 100644
--- a/internal/gojs/spfunc/spfunc_test.go
+++ b/internal/gojs/spfunc/spfunc_test.go
@@ -193,7 +193,7 @@ func i64i32i32i32i32_i64i32_withSP(vRef uint64, mAddr, mLen, argsArray, argsLen
 }
 
 func TestMustCallFromSP(t *testing.T) {
-	r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfigInterpreter().WithWasmCore2())
+	r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfigInterpreter())
 	defer r.Close(testCtx)
 
 	funcName := "i64i32i32i32i32_i64i32_withSP"
diff --git a/internal/integration_test/bench/codec_test.go b/internal/integration_test/bench/codec_test.go
index a186b52c..b6a1f44d 100644
--- a/internal/integration_test/bench/codec_test.go
+++ b/internal/integration_test/bench/codec_test.go
@@ -4,6 +4,7 @@ import (
 	"testing"
 
 	"github.com/tetratelabs/wazero"
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
@@ -88,13 +89,13 @@ func newExample() *wasm.Module {
 
 func TestExampleUpToDate(t *testing.T) {
 	t.Run("binary.DecodeModule", func(t *testing.T) {
-		m, err := binary.DecodeModule(exampleWasm, wasm.Features20220419, wasm.MemorySizer)
+		m, err := binary.DecodeModule(exampleWasm, api.CoreFeaturesV2, wasm.MemorySizer)
 		require.NoError(t, err)
 		require.Equal(t, example, m)
 	})
 
 	t.Run("Executable", func(t *testing.T) {
-		r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithWasmCore2())
+		r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig())
 
 		// Add WASI to satisfy import tests
 		wm, err := wasi_snapshot_preview1.Instantiate(testCtx, r)
@@ -117,7 +118,7 @@ func BenchmarkCodec(b *testing.B) {
 	b.Run("binary.DecodeModule", func(b *testing.B) {
 		b.ReportAllocs()
 		for i := 0; i < b.N; i++ {
-			if _, err := binary.DecodeModule(exampleWasm, wasm.Features20220419, wasm.MemorySizer); err != nil {
+			if _, err := binary.DecodeModule(exampleWasm, api.CoreFeaturesV2, wasm.MemorySizer); err != nil {
 				b.Fatal(err)
 			}
 		}
diff --git a/internal/integration_test/bench/hostfunc_bench_test.go b/internal/integration_test/bench/hostfunc_bench_test.go
index 95f525f1..67d07f7a 100644
--- a/internal/integration_test/bench/hostfunc_bench_test.go
+++ b/internal/integration_test/bench/hostfunc_bench_test.go
@@ -137,7 +137,7 @@ func getCallEngine(m *wasm.ModuleInstance, name string) (ce wasm.CallEngine, err
 }
 
 func setupHostCallBench(requireNoError func(error)) *wasm.ModuleInstance {
-	eng := compiler.NewEngine(context.Background(), wasm.Features20220419)
+	eng := compiler.NewEngine(context.Background(), api.CoreFeaturesV2)
 
 	ft := &wasm.FunctionType{
 		Params:           []wasm.ValueType{wasm.ValueTypeI32},
diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go
index 69485f8c..f94f3d6a 100644
--- a/internal/integration_test/engine/adhoc_test.go
+++ b/internal/integration_test/engine/adhoc_test.go
@@ -56,15 +56,14 @@ func TestEngineCompiler(t *testing.T) {
 	if !platform.CompilerSupported() {
 		t.Skip()
 	}
-	runAllTests(t, tests, wazero.NewRuntimeConfigCompiler().WithWasmCore2())
+	runAllTests(t, tests, wazero.NewRuntimeConfigCompiler())
 }
 
 func TestEngineInterpreter(t *testing.T) {
-	runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter().WithWasmCore2())
+	runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter())
 }
 
 func runAllTests(t *testing.T, tests map[string]func(t *testing.T, r wazero.Runtime), config wazero.RuntimeConfig) {
-	config = config.WithFeatureReferenceTypes(true)
 	for name, testf := range tests {
 		name := name   // pin
 		testf := testf // pin
@@ -416,7 +415,7 @@ func callReturnImportWasm(t *testing.T, importedModule, importingModule string,
 			},
 		},
 	}
-	require.NoError(t, module.Validate(wasm.Features20220419))
+	require.NoError(t, module.Validate(api.CoreFeaturesV2))
 	return binary.EncodeModule(module)
 }
 
@@ -452,7 +451,7 @@ func callOuterInnerWasm(t *testing.T, importedModule, importingModule string) []
 			},
 		},
 	}
-	require.NoError(t, module.Validate(wasm.Features20220419))
+	require.NoError(t, module.Validate(api.CoreFeaturesV2))
 	return binary.EncodeModule(module)
 }
 
diff --git a/internal/integration_test/fuzzcases/fuzzcases_test.go b/internal/integration_test/fuzzcases/fuzzcases_test.go
index f4902688..355b541c 100644
--- a/internal/integration_test/fuzzcases/fuzzcases_test.go
+++ b/internal/integration_test/fuzzcases/fuzzcases_test.go
@@ -27,7 +27,7 @@ func runWithCompiler(t *testing.T, runner func(t *testing.T, r wazero.Runtime))
 		return
 	}
 	t.Run("compiler", func(t *testing.T) {
-		r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigCompiler().WithWasmCore2())
+		r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigCompiler())
 		defer r.Close(ctx)
 		runner(t, r)
 	})
@@ -35,7 +35,7 @@ func runWithCompiler(t *testing.T, runner func(t *testing.T, r wazero.Runtime))
 
 func runWithInterpreter(t *testing.T, runner func(t *testing.T, r wazero.Runtime)) {
 	t.Run("interpreter", func(t *testing.T) {
-		r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter().WithWasmCore2())
+		r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
 		defer r.Close(ctx)
 		runner(t, r)
 	})
diff --git a/internal/integration_test/spectest/spectest.go b/internal/integration_test/spectest/spectest.go
index 7d6df55c..26877e84 100644
--- a/internal/integration_test/spectest/spectest.go
+++ b/internal/integration_test/spectest/spectest.go
@@ -326,8 +326,8 @@ var spectestWasm []byte
 //
 // See https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/imports.wast
 // See https://github.com/WebAssembly/spec/blob/wg-1.0/interpreter/script/js.ml#L13-L25
-func addSpectestModule(t *testing.T, ctx context.Context, s *wasm.Store, ns *wasm.Namespace, enabledFeatures wasm.Features) {
-	mod, err := binaryformat.DecodeModule(spectestWasm, wasm.Features20220419, wasm.MemorySizer)
+func addSpectestModule(t *testing.T, ctx context.Context, s *wasm.Store, ns *wasm.Namespace, enabledFeatures api.CoreFeatures) {
+	mod, err := binaryformat.DecodeModule(spectestWasm, api.CoreFeaturesV2, wasm.MemorySizer)
 	require.NoError(t, err)
 
 	// (global (export "global_i32") i32 (i32.const 666))
@@ -385,7 +385,7 @@ func maybeSetMemoryCap(mod *wasm.Module) {
 
 // Run runs all the test inside the testDataFS file system where all the cases are described
 // via JSON files created from wast2json.
-func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(context.Context, wasm.Features) wasm.Engine, enabledFeatures wasm.Features) {
+func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(context.Context, api.CoreFeatures) wasm.Engine, enabledFeatures api.CoreFeatures) {
 	files, err := testDataFS.ReadDir("testdata")
 	require.NoError(t, err)
 
@@ -839,7 +839,7 @@ func requireStripCustomSections(t *testing.T, binary []byte) []byte {
 
 // TestBinaryEncoder ensures that binary.EncodeModule produces exactly the same binaries
 // for wasm.Module via binary.DecodeModule modulo custom sections for all the valid binaries in spectests.
-func TestBinaryEncoder(t *testing.T, testDataFS embed.FS, enabledFeatures wasm.Features) {
+func TestBinaryEncoder(t *testing.T, testDataFS embed.FS, enabledFeatures api.CoreFeatures) {
 	files, err := testDataFS.ReadDir("testdata")
 	require.NoError(t, err)
 
diff --git a/internal/integration_test/spectest/v1/spectest.go b/internal/integration_test/spectest/v1/spectest.go
index a1acb45f..7d4fb394 100644
--- a/internal/integration_test/spectest/v1/spectest.go
+++ b/internal/integration_test/spectest/v1/spectest.go
@@ -3,7 +3,7 @@ package v1
 import (
 	"embed"
 
-	"github.com/tetratelabs/wazero/internal/wasm"
+	"github.com/tetratelabs/wazero/api"
 )
 
 // Testcases is exported for cross-process file cache tests.
@@ -13,4 +13,4 @@ import (
 var Testcases embed.FS
 
 // EnabledFeatures is exported for cross-process file cache tests.
-const EnabledFeatures = wasm.Features20191205
+const EnabledFeatures = api.CoreFeaturesV1
diff --git a/internal/integration_test/spectest/v2/spec_test.go b/internal/integration_test/spectest/v2/spec_test.go
index 4a4574ad..13719175 100644
--- a/internal/integration_test/spectest/v2/spec_test.go
+++ b/internal/integration_test/spectest/v2/spec_test.go
@@ -5,18 +5,18 @@ import (
 	"embed"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/engine/compiler"
 	"github.com/tetratelabs/wazero/internal/engine/interpreter"
 	"github.com/tetratelabs/wazero/internal/integration_test/spectest"
 	"github.com/tetratelabs/wazero/internal/platform"
-	"github.com/tetratelabs/wazero/internal/wasm"
 )
 
 //go:embed testdata/*.wasm
 //go:embed testdata/*.json
 var testcases embed.FS
 
-const enabledFeatures = wasm.Features20220419
+const enabledFeatures = api.CoreFeaturesV2
 
 func TestCompiler(t *testing.T) {
 	if !platform.CompilerSupported() {
diff --git a/internal/integration_test/vs/runtime.go b/internal/integration_test/vs/runtime.go
index c18f8fbd..fa00a0b9 100644
--- a/internal/integration_test/vs/runtime.go
+++ b/internal/integration_test/vs/runtime.go
@@ -46,11 +46,11 @@ type Module interface {
 }
 
 func NewWazeroInterpreterRuntime() Runtime {
-	return newWazeroRuntime("wazero-interpreter", wazero.NewRuntimeConfigInterpreter().WithWasmCore2())
+	return newWazeroRuntime("wazero-interpreter", wazero.NewRuntimeConfigInterpreter())
 }
 
 func NewWazeroCompilerRuntime() Runtime {
-	return newWazeroRuntime(compilerRuntime, wazero.NewRuntimeConfigCompiler().WithWasmCore2())
+	return newWazeroRuntime(compilerRuntime, wazero.NewRuntimeConfigCompiler())
 }
 
 func newWazeroRuntime(name string, config wazero.RuntimeConfig) *wazeroRuntime {
diff --git a/internal/testing/enginetest/enginetest.go b/internal/testing/enginetest/enginetest.go
index 7bfa942a..c23d02fc 100644
--- a/internal/testing/enginetest/enginetest.go
+++ b/internal/testing/enginetest/enginetest.go
@@ -46,7 +46,7 @@ type EngineTester interface {
 	// IsCompiler returns true if this engine is a compiler.
 	IsCompiler() bool
 
-	NewEngine(enabledFeatures wasm.Features) wasm.Engine
+	NewEngine(enabledFeatures api.CoreFeatures) wasm.Engine
 
 	ListenerFactory() experimental.FunctionListenerFactory
 
@@ -59,7 +59,7 @@ type EngineTester interface {
 }
 
 func RunTestEngine_NewModuleEngine(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20191205)
+	e := et.NewEngine(api.CoreFeaturesV1)
 
 	t.Run("error before instantiation", func(t *testing.T) {
 		_, err := e.NewModuleEngine("mymod", &wasm.Module{}, nil, nil, nil, nil)
@@ -77,7 +77,7 @@ func RunTestEngine_NewModuleEngine(t *testing.T, et EngineTester) {
 }
 
 func RunTestEngine_InitializeFuncrefGlobals(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20220419)
+	e := et.NewEngine(api.CoreFeaturesV2)
 
 	i64 := i64
 	m := &wasm.Module{
@@ -120,7 +120,7 @@ func RunTestEngine_InitializeFuncrefGlobals(t *testing.T, et EngineTester) {
 }
 
 func RunTestModuleEngine_Call(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20220419)
+	e := et.NewEngine(api.CoreFeaturesV2)
 
 	// Define a basic function which defines two parameters and two results.
 	// This is used to test results when incorrect arity is used.
@@ -180,7 +180,7 @@ func RunTestModuleEngine_Call(t *testing.T, et EngineTester) {
 }
 
 func RunTestEngine_NewModuleEngine_InitTable(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20191205)
+	e := et.NewEngine(api.CoreFeaturesV1)
 
 	t.Run("no table elements", func(t *testing.T) {
 		table := &wasm.TableInstance{Min: 2, References: make([]wasm.Reference, 2)}
@@ -347,7 +347,7 @@ func RunTestEngine_NewModuleEngine_InitTable(t *testing.T, et EngineTester) {
 }
 
 func runTestModuleEngine_Call_HostFn_Mem(t *testing.T, et EngineTester, readMem *wasm.Code) {
-	e := et.NewEngine(wasm.Features20191205)
+	e := et.NewEngine(api.CoreFeaturesV1)
 	_, importing, done := setupCallMemTests(t, e, readMem, et.ListenerFactory())
 	defer done()
 
@@ -396,7 +396,7 @@ func RunTestModuleEngine_Call_HostFn(t *testing.T, et EngineTester) {
 }
 
 func runTestModuleEngine_Call_HostFn(t *testing.T, et EngineTester, hostDivBy *wasm.Code) {
-	e := et.NewEngine(wasm.Features20191205)
+	e := et.NewEngine(api.CoreFeaturesV1)
 
 	_, imported, importing, done := setupCallTests(t, e, hostDivBy, et.ListenerFactory())
 	defer done()
@@ -441,7 +441,7 @@ func runTestModuleEngine_Call_HostFn(t *testing.T, et EngineTester, hostDivBy *w
 }
 
 func RunTestModuleEngine_Call_Errors(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20191205)
+	e := et.NewEngine(api.CoreFeaturesV1)
 
 	_, imported, importing, done := setupCallTests(t, e, hostDivByGo, et.ListenerFactory())
 	defer done()
@@ -548,7 +548,7 @@ wasm stack trace:
 // * Host code calls append on a byte slice returned by api.Memory Read
 // * Wasm code calls wasm.OpcodeMemoryGrowName and this changes the capacity (by default, it will).
 func RunTestModuleEngine_Memory(t *testing.T, et EngineTester) {
-	e := et.NewEngine(wasm.Features20220419)
+	e := et.NewEngine(api.CoreFeaturesV2)
 
 	wasmPhrase := "Well, that'll be the day when you say goodbye."
 	wasmPhraseSize := uint32(len(wasmPhrase))
diff --git a/internal/wasm/binary/const_expr_test.go b/internal/wasm/binary/const_expr_test.go
index 476101b8..e06ad571 100644
--- a/internal/wasm/binary/const_expr_test.go
+++ b/internal/wasm/binary/const_expr_test.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -84,7 +85,7 @@ func TestDecodeConstantExpression(t *testing.T) {
 		tc := tt
 		t.Run(strconv.Itoa(i), func(t *testing.T) {
 			actual, err := decodeConstantExpression(bytes.NewReader(tc.in),
-				wasm.FeatureBulkMemoryOperations|wasm.FeatureSIMD)
+				api.CoreFeatureBulkMemoryOperations|api.CoreFeatureSIMD)
 			require.NoError(t, err)
 			require.Equal(t, tc.exp, actual)
 		})
@@ -95,7 +96,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 	tests := []struct {
 		in          []byte
 		expectedErr string
-		features    wasm.Features
+		features    api.CoreFeatures
 	}{
 		{
 			in: []byte{
@@ -103,14 +104,14 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				0,
 			},
 			expectedErr: "look for end opcode: EOF",
-			features:    wasm.FeatureBulkMemoryOperations,
+			features:    api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
 				wasm.OpcodeRefNull,
 			},
 			expectedErr: "read reference type for ref.null: EOF",
-			features:    wasm.FeatureBulkMemoryOperations,
+			features:    api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
@@ -119,7 +120,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				wasm.OpcodeEnd,
 			},
 			expectedErr: "invalid type for ref.null: 0xff",
-			features:    wasm.FeatureBulkMemoryOperations,
+			features:    api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
@@ -128,7 +129,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				wasm.OpcodeEnd,
 			},
 			expectedErr: "ref.null is not supported as feature \"bulk-memory-operations\" is disabled",
-			features:    wasm.Features20191205,
+			features:    api.CoreFeaturesV1,
 		},
 		{
 			in: []byte{
@@ -137,7 +138,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				wasm.OpcodeEnd,
 			},
 			expectedErr: "ref.func is not supported as feature \"bulk-memory-operations\" is disabled",
-			features:    wasm.Features20191205,
+			features:    api.CoreFeaturesV1,
 		},
 		{
 			in: []byte{
@@ -148,14 +149,14 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				wasm.OpcodeEnd,
 			},
 			expectedErr: "vector instructions are not supported as feature \"simd\" is disabled",
-			features:    wasm.Features20191205,
+			features:    api.CoreFeaturesV1,
 		},
 		{
 			in: []byte{
 				wasm.OpcodeVecPrefix,
 			},
 			expectedErr: "read vector instruction opcode suffix: EOF",
-			features:    wasm.FeatureSIMD,
+			features:    api.CoreFeatureSIMD,
 		},
 		{
 			in: []byte{
@@ -165,7 +166,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				wasm.OpcodeEnd,
 			},
 			expectedErr: "invalid vector opcode for const expression: 0x1",
-			features:    wasm.FeatureSIMD,
+			features:    api.CoreFeatureSIMD,
 		},
 		{
 			in: []byte{
@@ -174,7 +175,7 @@ func TestDecodeConstantExpression_errors(t *testing.T) {
 				1, 1, 1, 1, 1, 1, 1, 1,
 			},
 			expectedErr: "read vector const instruction immediates: needs 16 bytes but was 8 bytes",
-			features:    wasm.FeatureSIMD,
+			features:    api.CoreFeatureSIMD,
 		},
 	}
 
diff --git a/internal/wasm/binary/data_test.go b/internal/wasm/binary/data_test.go
index e952f2e1..518b6206 100644
--- a/internal/wasm/binary/data_test.go
+++ b/internal/wasm/binary/data_test.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -13,7 +14,7 @@ func Test_decodeDataSegment(t *testing.T) {
 	tests := []struct {
 		in       []byte
 		exp      *wasm.DataSegment
-		features wasm.Features
+		features api.CoreFeatures
 		expErr   string
 	}{
 		{
@@ -23,7 +24,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				// Two initial data.
 				0x2, 0xf, 0xf,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 			expErr:   "invalid data segment prefix: 0xf",
 		},
 		{
@@ -40,7 +41,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				},
 				Init: []byte{0xf, 0xf},
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x0,
@@ -49,7 +50,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				0x2, 0xf, 0xf,
 			},
 			expErr:   "read offset expression: constant expression has been not terminated",
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x1, // Passive data segment without memory index and const expr.
@@ -60,7 +61,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				OffsetExpression: nil,
 				Init:             []byte{0xf, 0xf},
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x2,
@@ -77,7 +78,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				},
 				Init: []byte{0xf, 0xf},
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x2,
@@ -88,7 +89,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				0x2, 0xf, 0xf,
 			},
 			expErr:   "memory index must be zero but was 1",
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x2,
@@ -99,7 +100,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				0x2, 0xf, 0xf,
 			},
 			expErr:   "read offset expression: constant expression has been not terminated",
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{0x2,
@@ -109,7 +110,7 @@ func Test_decodeDataSegment(t *testing.T) {
 				// Two initial data.
 				0x2, 0xf, 0xf,
 			},
-			features: wasm.FeatureMutableGlobal,
+			features: api.CoreFeatureMutableGlobal,
 			expErr:   "non-zero prefix for data segment is invalid as feature \"bulk-memory-operations\" is disabled",
 		},
 	}
diff --git a/internal/wasm/binary/decoder_test.go b/internal/wasm/binary/decoder_test.go
index a55c28fc..1b990e62 100644
--- a/internal/wasm/binary/decoder_test.go
+++ b/internal/wasm/binary/decoder_test.go
@@ -3,6 +3,7 @@ package binary
 import (
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -80,7 +81,7 @@ func TestDecodeModule(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			m, e := DecodeModule(EncodeModule(tc.input), wasm.Features20191205, wasm.MemorySizer)
+			m, e := DecodeModule(EncodeModule(tc.input), api.CoreFeaturesV1, wasm.MemorySizer)
 			require.NoError(t, e)
 			require.Equal(t, tc.input, m)
 		})
@@ -91,7 +92,7 @@ func TestDecodeModule(t *testing.T) {
 			wasm.SectionIDCustom, 0xf, // 15 bytes in this section
 			0x04, 'm', 'e', 'm', 'e',
 			1, 2, 3, 4, 5, 6, 7, 8, 9, 0)
-		m, e := DecodeModule(input, wasm.Features20191205, wasm.MemorySizer)
+		m, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemorySizer)
 		require.NoError(t, e)
 		require.Equal(t, &wasm.Module{}, m)
 	})
@@ -106,14 +107,14 @@ func TestDecodeModule(t *testing.T) {
 			subsectionIDModuleName, 0x07, // 7 bytes in this subsection
 			0x06, // the Module name simple is 6 bytes long
 			's', 'i', 'm', 'p', 'l', 'e')
-		m, e := DecodeModule(input, wasm.Features20191205, wasm.MemorySizer)
+		m, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemorySizer)
 		require.NoError(t, e)
 		require.Equal(t, &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "simple"}}, m)
 	})
 	t.Run("data count section disabled", func(t *testing.T) {
 		input := append(append(Magic, version...),
 			wasm.SectionIDDataCount, 1, 0)
-		_, e := DecodeModule(input, wasm.Features20191205, wasm.MemorySizer)
+		_, e := DecodeModule(input, api.CoreFeaturesV1, wasm.MemorySizer)
 		require.EqualError(t, e, `data count section not supported as feature "bulk-memory-operations" is disabled`)
 	})
 }
@@ -163,7 +164,7 @@ func TestDecodeModule_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			_, e := DecodeModule(tc.input, wasm.Features20191205, wasm.MemorySizer)
+			_, e := DecodeModule(tc.input, api.CoreFeaturesV1, wasm.MemorySizer)
 			require.EqualError(t, e, tc.expectedErr)
 		})
 	}
diff --git a/internal/wasm/binary/element_test.go b/internal/wasm/binary/element_test.go
index 5b642995..84ee7331 100644
--- a/internal/wasm/binary/element_test.go
+++ b/internal/wasm/binary/element_test.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -48,13 +49,13 @@ func Test_decodeElementConstExprVector(t *testing.T) {
 		in       []byte
 		refType  wasm.RefType
 		exp      []*wasm.Index
-		features wasm.Features
+		features api.CoreFeatures
 	}{
 		{
 			in:       []byte{0},
 			exp:      []*wasm.Index{},
 			refType:  wasm.RefTypeFuncref,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
@@ -64,7 +65,7 @@ func Test_decodeElementConstExprVector(t *testing.T) {
 			},
 			exp:      []*wasm.Index{nil, uint32Ptr(100)},
 			refType:  wasm.RefTypeFuncref,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
@@ -77,7 +78,7 @@ func Test_decodeElementConstExprVector(t *testing.T) {
 			},
 			exp:      []*wasm.Index{nil, uint32Ptr(165675008), nil},
 			refType:  wasm.RefTypeFuncref,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			in: []byte{
@@ -87,7 +88,7 @@ func Test_decodeElementConstExprVector(t *testing.T) {
 			},
 			exp:      []*wasm.Index{nil, nil},
 			refType:  wasm.RefTypeExternref,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 	}
 
@@ -107,7 +108,7 @@ func Test_decodeElementConstExprVector_errors(t *testing.T) {
 		in       []byte
 		refType  wasm.RefType
 		expErr   string
-		features wasm.Features
+		features api.CoreFeatures
 	}{
 		{
 			name:   "eof",
@@ -122,28 +123,28 @@ func Test_decodeElementConstExprVector_errors(t *testing.T) {
 			name:     "type mismatch - ref.null",
 			in:       []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd},
 			refType:  wasm.RefTypeFuncref,
-			features: wasm.Features20220419,
+			features: api.CoreFeaturesV2,
 			expErr:   "element type mismatch: want funcref, but constexpr has externref",
 		},
 		{
 			name:     "type mismatch - ref.null",
 			in:       []byte{1, wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd},
 			refType:  wasm.RefTypeExternref,
-			features: wasm.Features20220419,
+			features: api.CoreFeaturesV2,
 			expErr:   "element type mismatch: want externref, but constexpr has funcref",
 		},
 		{
 			name:     "invalid ref type",
 			in:       []byte{1, wasm.OpcodeRefNull, 0xff, wasm.OpcodeEnd},
 			refType:  wasm.RefTypeExternref,
-			features: wasm.Features20220419,
+			features: api.CoreFeaturesV2,
 			expErr:   "invalid type for ref.null: 0xff",
 		},
 		{
 			name:     "type mismatch - ref.fuc",
 			in:       []byte{1, wasm.OpcodeRefFunc, 0, wasm.OpcodeEnd},
 			refType:  wasm.RefTypeExternref,
-			features: wasm.Features20220419,
+			features: api.CoreFeaturesV2,
 			expErr:   "element type mismatch: want externref, but constexpr has funcref",
 		},
 	}
@@ -163,7 +164,7 @@ func TestDecodeElementSegment(t *testing.T) {
 		in       []byte
 		exp      *wasm.ElementSegment
 		expErr   string
-		features wasm.Features
+		features api.CoreFeatures
 	}{
 		{
 			name: "legacy",
@@ -180,7 +181,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode:       wasm.ElementModeActive,
 				Type:       wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "legacy multi byte const expr data",
@@ -197,7 +198,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode:       wasm.ElementModeActive,
 				Type:       wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 
@@ -213,7 +214,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode: wasm.ElementModePassive,
 				Type: wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "active with table index encoded.",
@@ -232,7 +233,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode:       wasm.ElementModeActive,
 				Type:       wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 
@@ -253,7 +254,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Type:       wasm.RefTypeFuncref,
 				TableIndex: 10,
 			},
-			features: wasm.FeatureBulkMemoryOperations | wasm.FeatureReferenceTypes,
+			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 		},
 		{
 
@@ -268,7 +269,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				5, 1, 2, 3, 4, 5,
 			},
 			expErr:   `table index must be zero but was 10: feature "reference-types" is disabled`,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "declarative",
@@ -283,7 +284,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode: wasm.ElementModeDeclarative,
 				Type: wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "active const expr vector",
@@ -305,7 +306,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode:       wasm.ElementModeActive,
 				Type:       wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "passive const expr vector - funcref",
@@ -325,7 +326,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode: wasm.ElementModePassive,
 				Type: wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "passive const expr vector - unknown ref type",
@@ -334,7 +335,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				0xff,
 			},
 			expErr:   `ref type must be funcref or externref for element as of WebAssembly 2.0`,
-			features: wasm.FeatureBulkMemoryOperations | wasm.FeatureReferenceTypes,
+			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "active with table index and const expr vector",
@@ -358,7 +359,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode:       wasm.ElementModeActive,
 				Type:       wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "active with non zero table index and const expr vector",
@@ -383,7 +384,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Type:       wasm.RefTypeFuncref,
 				TableIndex: 10,
 			},
-			features: wasm.FeatureBulkMemoryOperations | wasm.FeatureReferenceTypes,
+			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "active with non zero table index and const expr vector but feature disabled",
@@ -402,7 +403,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
 			},
 			expErr:   `table index must be zero but was 10: feature "reference-types" is disabled`,
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 		{
 			name: "declarative const expr vector",
@@ -421,7 +422,7 @@ func TestDecodeElementSegment(t *testing.T) {
 				Mode: wasm.ElementModeDeclarative,
 				Type: wasm.RefTypeFuncref,
 			},
-			features: wasm.FeatureBulkMemoryOperations,
+			features: api.CoreFeatureBulkMemoryOperations,
 		},
 	}
 
@@ -440,6 +441,6 @@ func TestDecodeElementSegment(t *testing.T) {
 }
 
 func TestDecodeElementSegment_errors(t *testing.T) {
-	_, err := decodeElementSegment(bytes.NewReader([]byte{1}), wasm.FeatureMultiValue)
+	_, err := decodeElementSegment(bytes.NewReader([]byte{1}), api.CoreFeatureMultiValue)
 	require.EqualError(t, err, `non-zero prefix for element segment is invalid as feature "bulk-memory-operations" is disabled`)
 }
diff --git a/internal/wasm/binary/function_test.go b/internal/wasm/binary/function_test.go
index 8acbdf32..46aad1a5 100644
--- a/internal/wasm/binary/function_test.go
+++ b/internal/wasm/binary/function_test.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -82,7 +83,7 @@ func TestFunctionType(t *testing.T) {
 		})
 
 		t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) {
-			binary, err := decodeFunctionType(wasm.Features20220419, bytes.NewReader(b))
+			binary, err := decodeFunctionType(api.CoreFeaturesV2, bytes.NewReader(b))
 			require.NoError(t, err)
 			require.Equal(t, binary, tc.input)
 		})
@@ -94,7 +95,7 @@ func TestDecodeFunctionType_Errors(t *testing.T) {
 	tests := []struct {
 		name            string
 		input           []byte
-		enabledFeatures wasm.Features
+		enabledFeatures api.CoreFeatures
 		expectedErr     string
 	}{
 		{
@@ -133,7 +134,7 @@ func TestDecodeFunctionType_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			_, err := decodeFunctionType(wasm.Features20191205, bytes.NewReader(tc.input))
+			_, err := decodeFunctionType(api.CoreFeaturesV1, bytes.NewReader(tc.input))
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
diff --git a/internal/wasm/binary/section_test.go b/internal/wasm/binary/section_test.go
index 7d129057..2da3e5a4 100644
--- a/internal/wasm/binary/section_test.go
+++ b/internal/wasm/binary/section_test.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -43,7 +44,7 @@ func TestTableSection(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			tables, err := decodeTableSection(bytes.NewReader(tc.input), wasm.FeatureReferenceTypes)
+			tables, err := decodeTableSection(bytes.NewReader(tc.input), api.CoreFeatureReferenceTypes)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, tables)
 		})
@@ -55,7 +56,7 @@ func TestTableSection_Errors(t *testing.T) {
 		name        string
 		input       []byte
 		expectedErr string
-		features    wasm.Features
+		features    api.CoreFeatures
 	}{
 		{
 			name: "min and min with max",
@@ -65,7 +66,7 @@ func TestTableSection_Errors(t *testing.T) {
 				wasm.RefTypeFuncref, 0x01, 0x02, 0x03, // (table 2 3)
 			},
 			expectedErr: "at most one table allowed in module as feature \"reference-types\" is disabled",
-			features:    wasm.Features20191205,
+			features:    api.CoreFeaturesV1,
 		},
 	}
 
diff --git a/internal/wasm/binary/table_test.go b/internal/wasm/binary/table_test.go
index c1da684b..a91000df 100644
--- a/internal/wasm/binary/table_test.go
+++ b/internal/wasm/binary/table_test.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 	"github.com/tetratelabs/wazero/internal/wasm"
 )
@@ -59,7 +60,7 @@ func TestTableType(t *testing.T) {
 		})
 
 		t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) {
-			decoded, err := decodeTable(bytes.NewReader(b), wasm.FeatureReferenceTypes)
+			decoded, err := decodeTable(bytes.NewReader(b), api.CoreFeatureReferenceTypes)
 			require.NoError(t, err)
 			require.Equal(t, decoded, tc.input)
 		})
@@ -71,7 +72,7 @@ func TestDecodeTableType_Errors(t *testing.T) {
 		name        string
 		input       []byte
 		expectedErr string
-		features    wasm.Features
+		features    api.CoreFeatures
 	}{
 		{
 			name:        "not func ref",
@@ -82,13 +83,13 @@ func TestDecodeTableType_Errors(t *testing.T) {
 			name:        "max < min",
 			input:       []byte{wasm.RefTypeFuncref, 0x1, 0x80, 0x80, 0x4, 0},
 			expectedErr: "table size minimum must not be greater than maximum",
-			features:    wasm.FeatureReferenceTypes,
+			features:    api.CoreFeatureReferenceTypes,
 		},
 		{
 			name:        "min > limit",
 			input:       []byte{wasm.RefTypeFuncref, 0x0, 0xff, 0xff, 0xff, 0xff, 0xf},
 			expectedErr: "table min must be at most 134217728",
-			features:    wasm.FeatureReferenceTypes,
+			features:    api.CoreFeatureReferenceTypes,
 		},
 	}
 
diff --git a/internal/wasm/features_test.go b/internal/wasm/features_test.go
deleted file mode 100644
index 59dd7211..00000000
--- a/internal/wasm/features_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package wasm
-
-import (
-	"testing"
-
-	"github.com/tetratelabs/wazero/internal/testing/require"
-)
-
-// TestFeatures_ZeroIsInvalid reminds maintainers that a bitset cannot use zero as a flag!
-// This is why we start iota with 1.
-func TestFeatures_ZeroIsInvalid(t *testing.T) {
-	f := Features(0)
-	f = f.Set(0, true)
-	require.False(t, f.Get(0))
-}
-
-// TestFeatures tests the bitset works as expected
-func TestFeatures(t *testing.T) {
-	tests := []struct {
-		name    string
-		feature Features
-	}{
-		{
-			name:    "one is the smallest flag",
-			feature: 1,
-		},
-		{
-			name:    "63 is the largest feature flag", // because uint64
-			feature: 1 << 63,
-		},
-	}
-
-	for _, tt := range tests {
-		tc := tt
-
-		t.Run(tc.name, func(t *testing.T) {
-			f := Features(0)
-
-			// Defaults to false
-			require.False(t, f.Get(tc.feature))
-
-			// Set true makes it true
-			f = f.Set(tc.feature, true)
-			require.True(t, f.Get(tc.feature))
-
-			// Set false makes it false again
-			f = f.Set(tc.feature, false)
-			require.False(t, f.Get(tc.feature))
-		})
-	}
-}
-
-func TestFeatures_String(t *testing.T) {
-	tests := []struct {
-		name     string
-		feature  Features
-		expected string
-	}{
-		{name: "none", feature: 0, expected: ""},
-		{name: "mutable-global", feature: FeatureMutableGlobal, expected: "mutable-global"},
-		{name: "sign-extension-ops", feature: FeatureSignExtensionOps, expected: "sign-extension-ops"},
-		{name: "multi-value", feature: FeatureMultiValue, expected: "multi-value"},
-		{name: "simd", feature: FeatureSIMD, expected: "simd"},
-		{name: "features", feature: FeatureMutableGlobal | FeatureMultiValue, expected: "multi-value|mutable-global"},
-		{name: "undefined", feature: 1 << 63, expected: ""},
-		{name: "2.0", feature: Features20220419,
-			expected: "bulk-memory-operations|multi-value|mutable-global|" +
-				"nontrapping-float-to-int-conversion|reference-types|sign-extension-ops|simd"},
-	}
-
-	for _, tt := range tests {
-		tc := tt
-		t.Run(tc.name, func(t *testing.T) {
-			require.Equal(t, tc.expected, tc.feature.String())
-		})
-	}
-}
-
-func TestFeatures_Require(t *testing.T) {
-	tests := []struct {
-		name        string
-		feature     Features
-		expectedErr string
-	}{
-		{name: "none", feature: 0, expectedErr: "feature \"mutable-global\" is disabled"},
-		{name: "mutable-global", feature: FeatureMutableGlobal},
-		{name: "sign-extension-ops", feature: FeatureSignExtensionOps, expectedErr: "feature \"mutable-global\" is disabled"},
-		{name: "multi-value", feature: FeatureMultiValue, expectedErr: "feature \"mutable-global\" is disabled"},
-		{name: "undefined", feature: 1 << 63, expectedErr: "feature \"mutable-global\" is disabled"},
-	}
-
-	for _, tt := range tests {
-		tc := tt
-		t.Run(tc.name, func(t *testing.T) {
-			err := tc.feature.Require(FeatureMutableGlobal)
-			if tc.expectedErr != "" {
-				require.EqualError(t, err, tc.expectedErr)
-			} else {
-				require.NoError(t, err)
-			}
-		})
-	}
-}
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index 108a22fe..9ee0320e 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -34,11 +35,11 @@ func TestModule_ValidateFunction_validateFunctionWithMaxStackValues(t *testing.T
 	}
 
 	t.Run("not exceed", func(t *testing.T) {
-		err := m.validateFunctionWithMaxStackValues(Features20191205, 0, []Index{0}, nil, nil, nil, max+1, nil)
+		err := m.validateFunctionWithMaxStackValues(api.CoreFeaturesV1, 0, []Index{0}, nil, nil, nil, max+1, nil)
 		require.NoError(t, err)
 	})
 	t.Run("exceed", func(t *testing.T) {
-		err := m.validateFunctionWithMaxStackValues(Features20191205, 0, []Index{0}, nil, nil, nil, max, nil)
+		err := m.validateFunctionWithMaxStackValues(api.CoreFeaturesV1, 0, []Index{0}, nil, nil, nil, max, nil)
 		require.Error(t, err)
 		expMsg := fmt.Sprintf("function may have %d stack values, which exceeds limit %d", valuesNum, max)
 		require.Equal(t, expMsg, err.Error())
@@ -81,7 +82,7 @@ func TestModule_ValidateFunction_SignExtensionOps(t *testing.T) {
 					FunctionSection: []Index{0},
 					CodeSection:     []*Code{{Body: []byte{tc.input}}},
 				}
-				err := m.validateFunction(Features20191205, 0, []Index{0}, nil, nil, nil, nil)
+				err := m.validateFunction(api.CoreFeaturesV1, 0, []Index{0}, nil, nil, nil, nil)
 				require.EqualError(t, err, tc.expectedErrOnDisable)
 			})
 			t.Run("enabled", func(t *testing.T) {
@@ -98,7 +99,7 @@ func TestModule_ValidateFunction_SignExtensionOps(t *testing.T) {
 					FunctionSection: []Index{0},
 					CodeSection:     []*Code{{Body: body}},
 				}
-				err := m.validateFunction(FeatureSignExtensionOps, 0, []Index{0}, nil, nil, nil, nil)
+				err := m.validateFunction(api.CoreFeatureSignExtensionOps, 0, []Index{0}, nil, nil, nil, nil)
 				require.NoError(t, err)
 			})
 		})
@@ -153,7 +154,7 @@ func TestModule_ValidateFunction_NonTrappingFloatToIntConversion(t *testing.T) {
 					FunctionSection: []Index{0},
 					CodeSection:     []*Code{{Body: []byte{OpcodeMiscPrefix, tc.input}}},
 				}
-				err := m.validateFunction(Features20191205, 0, []Index{0}, nil, nil, nil, nil)
+				err := m.validateFunction(api.CoreFeaturesV1, 0, []Index{0}, nil, nil, nil, nil)
 				require.EqualError(t, err, tc.expectedErrOnDisable)
 			})
 			t.Run("enabled", func(t *testing.T) {
@@ -171,7 +172,7 @@ func TestModule_ValidateFunction_NonTrappingFloatToIntConversion(t *testing.T) {
 					FunctionSection: []Index{0},
 					CodeSection:     []*Code{{Body: body}},
 				}
-				err := m.validateFunction(FeatureNonTrappingFloatToIntConversion, 0, []Index{0}, nil, nil, nil, nil)
+				err := m.validateFunction(api.CoreFeatureNonTrappingFloatToIntConversion, 0, []Index{0}, nil, nil, nil, nil)
 				require.NoError(t, err)
 			})
 		})
@@ -248,11 +249,11 @@ func TestModule_ValidateFunction_MultiValue(t *testing.T) {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
 			t.Run("disabled", func(t *testing.T) {
-				err := tc.module.validateFunction(Features20191205, 0, []Index{0}, nil, nil, nil, nil)
+				err := tc.module.validateFunction(api.CoreFeaturesV1, 0, []Index{0}, nil, nil, nil, nil)
 				require.EqualError(t, err, tc.expectedErrOnDisable)
 			})
 			t.Run("enabled", func(t *testing.T) {
-				err := tc.module.validateFunction(FeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
+				err := tc.module.validateFunction(api.CoreFeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
 				require.NoError(t, err)
 			})
 		})
@@ -289,7 +290,7 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 					ElementSection:   []*ElementSegment{{}},
 					DataCountSection: &c,
 				}
-				err := m.validateFunction(FeatureBulkMemoryOperations, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
+				err := m.validateFunction(api.CoreFeatureBulkMemoryOperations, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
 				require.NoError(t, err)
 			})
 		}
@@ -302,71 +303,71 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 			dataCountSectionNil bool
 			memory              *Memory
 			tables              []*Table
-			flag                Features
+			flag                api.CoreFeatures
 			expectedErr         string
 		}{
 			// memory.init
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      nil,
 				expectedErr: "memory must exist for memory.init",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				expectedErr: `memory.init invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:                []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit},
-				flag:                FeatureBulkMemoryOperations,
+				flag:                api.CoreFeatureBulkMemoryOperations,
 				dataCountSectionNil: true,
 				expectedErr:         `memory must exist for memory.init`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "failed to read data segment index for memory.init: EOF",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit, 100 /* data section out of range */},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "index 100 out of range of data section(len=1)",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "failed to read memory index for memory.init: EOF",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit, 0, 1},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "memory.init reserved byte must be zero encoded with 1 byte",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryInit, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "cannot pop the operand for memory.init: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryInit, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "cannot pop the operand for memory.init: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryInit, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "cannot pop the operand for memory.init: i32 missing",
@@ -374,25 +375,25 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 			// data.drop
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscDataDrop},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				expectedErr: `data.drop invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:                []byte{OpcodeMiscPrefix, OpcodeMiscDataDrop},
 				dataCountSectionNil: true,
 				memory:              &Memory{},
-				flag:                FeatureBulkMemoryOperations,
+				flag:                api.CoreFeatureBulkMemoryOperations,
 				expectedErr:         `data.drop requires data count section`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscDataDrop},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "failed to read data segment index for data.drop: EOF",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscDataDrop, 100 /* data section out of range */},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				dataSection: []*DataSegment{{}},
 				expectedErr: "index 100 out of range of data section(len=1)",
@@ -400,158 +401,158 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 			// memory.copy
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      nil,
 				expectedErr: "memory must exist for memory.copy",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				expectedErr: `memory.copy invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: `failed to read memory index for memory.copy: EOF`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "failed to read memory index for memory.copy: EOF",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy, 0, 1},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "memory.copy reserved byte must be zero encoded with 1 byte",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.copy: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.copy: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.copy: i32 missing",
 			},
 			// memory.fill
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryFill},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      nil,
 				expectedErr: "memory must exist for memory.fill",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryFill},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				expectedErr: `memory.fill invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryFill},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: `failed to read memory index for memory.fill: EOF`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryFill, 1},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: `memory.fill reserved byte must be zero encoded with 1 byte`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscMemoryFill, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.fill: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryFill, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.fill: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscMemoryFill, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				memory:      &Memory{},
 				expectedErr: "cannot pop the operand for memory.fill: i32 missing",
 			},
 			// table.init
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableInit},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				tables:      []*Table{{}},
 				expectedErr: `table.init invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableInit},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "failed to read element segment index for table.init: EOF",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 100 /* data section out of range */},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "index 100 out of range of element section(len=1)",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 0},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "failed to read source table index for table.init: EOF",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 10},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "source table index must be zero for table.init as feature \"reference-types\" is disabled",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 10},
-				flag:           FeatureBulkMemoryOperations | FeatureReferenceTypes,
+				flag:           api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "table of index 10 not found",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 1},
-				flag:           FeatureBulkMemoryOperations | FeatureReferenceTypes,
+				flag:           api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 				tables:         []*Table{{}, {Type: RefTypeExternref}},
 				elementSection: []*ElementSegment{{Type: RefTypeFuncref}},
 				expectedErr:    "type mismatch for table.init: element type funcref does not match table type externref",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 0},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "cannot pop the operand for table.init: i32 missing",
 			},
 			{
 				body:           []byte{OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 0},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "cannot pop the operand for table.init: i32 missing",
 			},
 			{
 				body:           []byte{OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscTableInit, 0, 0},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "cannot pop the operand for table.init: i32 missing",
@@ -559,19 +560,19 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 			// elem.drop
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscElemDrop},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				tables:      []*Table{{}},
 				expectedErr: `elem.drop invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscElemDrop},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "failed to read element segment index for elem.drop: EOF",
 			},
 			{
 				body:           []byte{OpcodeMiscPrefix, OpcodeMiscElemDrop, 100 /* element section out of range */},
-				flag:           FeatureBulkMemoryOperations,
+				flag:           api.CoreFeatureBulkMemoryOperations,
 				tables:         []*Table{{}},
 				elementSection: []*ElementSegment{{}},
 				expectedErr:    "index 100 out of range of element section(len=1)",
@@ -579,61 +580,61 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 			// table.copy
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy},
-				flag:        Features20191205,
+				flag:        api.CoreFeaturesV1,
 				tables:      []*Table{{}},
 				expectedErr: `table.copy invalid as feature "bulk-memory-operations" is disabled`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: `failed to read destination table index for table.copy: EOF`,
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 10},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "destination table index must be zero for table.copy as feature \"reference-types\" is disabled",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 3},
-				flag:        FeatureBulkMemoryOperations | FeatureReferenceTypes,
+				flag:        api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 				tables:      []*Table{{}, {}},
 				expectedErr: "table of index 3 not found",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 3},
-				flag:        FeatureBulkMemoryOperations | FeatureReferenceTypes,
+				flag:        api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 				tables:      []*Table{{}, {}, {}, {}},
 				expectedErr: "failed to read source table index for table.copy: EOF",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 0, 3},
-				flag:        FeatureBulkMemoryOperations, // Multiple tables require FeatureReferenceTypes.
+				flag:        api.CoreFeatureBulkMemoryOperations, // Multiple tables require api.CoreFeatureReferenceTypes.
 				tables:      []*Table{{}, {}, {}, {}},
 				expectedErr: "source table index must be zero for table.copy as feature \"reference-types\" is disabled",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 3, 1},
-				flag:        FeatureBulkMemoryOperations | FeatureReferenceTypes,
+				flag:        api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
 				tables:      []*Table{{}, {Type: RefTypeFuncref}, {}, {Type: RefTypeExternref}},
 				expectedErr: "table type mismatch for table.copy: funcref (src) != externref (dst)",
 			},
 			{
 				body:        []byte{OpcodeMiscPrefix, OpcodeMiscTableCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "cannot pop the operand for table.copy: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscTableCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "cannot pop the operand for table.copy: i32 missing",
 			},
 			{
 				body:        []byte{OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeMiscPrefix, OpcodeMiscTableCopy, 0, 0},
-				flag:        FeatureBulkMemoryOperations,
+				flag:        api.CoreFeatureBulkMemoryOperations,
 				tables:      []*Table{{}},
 				expectedErr: "cannot pop the operand for table.copy: i32 missing",
 			},
@@ -686,7 +687,7 @@ func TestModule_ValidateFunction_MultiValue_TypeMismatch(t *testing.T) {
 		name            string
 		module          *Module
 		expectedErr     string
-		enabledFeatures Features
+		enabledFeatures api.CoreFeatures
 	}{
 		// test/core/func.wast
 
@@ -2181,7 +2182,7 @@ func TestModule_ValidateFunction_MultiValue_TypeMismatch(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			err := tc.module.validateFunction(FeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
+			err := tc.module.validateFunction(api.CoreFeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
@@ -2198,7 +2199,7 @@ func TestModule_funcValidation_CallIndirect(t *testing.T) {
 				OpcodeEnd,
 			}}},
 		}
-		err := m.validateFunction(FeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{Type: RefTypeFuncref}}, nil)
+		err := m.validateFunction(api.CoreFeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{Type: RefTypeFuncref}}, nil)
 		require.NoError(t, err)
 	})
 	t.Run("non zero table index", func(t *testing.T) {
@@ -2212,11 +2213,11 @@ func TestModule_funcValidation_CallIndirect(t *testing.T) {
 			}}},
 		}
 		t.Run("disabled", func(t *testing.T) {
-			err := m.validateFunction(Features20191205, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
+			err := m.validateFunction(api.CoreFeaturesV1, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
 			require.EqualError(t, err, "table index must be zero but was 100: feature \"reference-types\" is disabled")
 		})
 		t.Run("enabled but out of range", func(t *testing.T) {
-			err := m.validateFunction(FeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
+			err := m.validateFunction(api.CoreFeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{}, {}}, nil)
 			require.EqualError(t, err, "unknown table index: 100")
 		})
 	})
@@ -2230,7 +2231,7 @@ func TestModule_funcValidation_CallIndirect(t *testing.T) {
 				OpcodeEnd,
 			}}},
 		}
-		err := m.validateFunction(FeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{Type: RefTypeExternref}}, nil)
+		err := m.validateFunction(api.CoreFeatureReferenceTypes, 0, []Index{0}, nil, &Memory{}, []*Table{{Type: RefTypeExternref}}, nil)
 		require.EqualError(t, err, "table is not funcref type but was externref for call_indirect")
 	})
 }
@@ -2239,13 +2240,13 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 	tests := []struct {
 		name                    string
 		body                    []byte
-		flag                    Features
+		flag                    api.CoreFeatures
 		declaredFunctionIndexes map[Index]struct{}
 		expectedErr             string
 	}{
 		{
 			name: "ref.null (funcref)",
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 			body: []byte{
 				OpcodeRefNull, ValueTypeFuncref,
 				OpcodeDrop, OpcodeEnd,
@@ -2253,7 +2254,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name: "ref.null (externref)",
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 			body: []byte{
 				OpcodeRefNull, ValueTypeExternref,
 				OpcodeDrop, OpcodeEnd,
@@ -2261,7 +2262,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name: "ref.null - disabled",
-			flag: Features20191205,
+			flag: api.CoreFeaturesV1,
 			body: []byte{
 				OpcodeRefNull, ValueTypeFuncref,
 				OpcodeDrop, OpcodeEnd,
@@ -2270,7 +2271,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name: "ref.is_null",
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 			body: []byte{
 				OpcodeRefNull, ValueTypeFuncref,
 				OpcodeRefIsNull,
@@ -2279,7 +2280,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name: "ref.is_null - disabled",
-			flag: Features20191205,
+			flag: api.CoreFeaturesV1,
 			body: []byte{
 				OpcodeRefIsNull,
 				OpcodeDrop, OpcodeEnd,
@@ -2288,7 +2289,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name:                    "ref.func",
-			flag:                    FeatureReferenceTypes,
+			flag:                    api.CoreFeatureReferenceTypes,
 			declaredFunctionIndexes: map[uint32]struct{}{0: {}},
 			body: []byte{
 				OpcodeRefFunc, 0,
@@ -2297,7 +2298,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name:                    "ref.func - undeclared function index",
-			flag:                    FeatureReferenceTypes,
+			flag:                    api.CoreFeatureReferenceTypes,
 			declaredFunctionIndexes: map[uint32]struct{}{0: {}},
 			body: []byte{
 				OpcodeRefFunc, 100,
@@ -2307,7 +2308,7 @@ func TestModule_funcValidation_RefTypes(t *testing.T) {
 		},
 		{
 			name:                    "ref.func",
-			flag:                    Features20191205,
+			flag:                    api.CoreFeaturesV1,
 			declaredFunctionIndexes: map[uint32]struct{}{0: {}},
 			body: []byte{
 				OpcodeRefFunc, 0,
@@ -2340,7 +2341,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 	tests := []struct {
 		name        string
 		body        []byte
-		flag        Features
+		flag        api.CoreFeatures
 		expectedErr string
 	}{
 		{
@@ -2353,7 +2354,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.grow (funcref) - type mismatch",
@@ -2364,7 +2365,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				1, // Table of externref type -> mismatch.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.grow: type mismatch: expected externref, but was funcref`,
 		},
 		{
@@ -2377,7 +2378,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.grow (externref) type mismatch",
@@ -2388,7 +2389,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				0, // Table of funcref type -> mismatch.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.grow: type mismatch: expected funcref, but was externref`,
 		},
 		{
@@ -2400,7 +2401,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				10, // Table Index.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `table of index 10 not found`,
 		},
 		{
@@ -2410,7 +2411,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				10, // Table Index.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `table of index 10 not found`,
 		},
 		{
@@ -2421,7 +2422,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.fill (funcref)",
@@ -2433,7 +2434,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				0, // Table Index.
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.fill (funcref) - type mismatch",
@@ -2445,7 +2446,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				1, // Table of externref type -> mismatch.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.fill: type mismatch: expected externref, but was funcref`,
 		},
 		{
@@ -2458,7 +2459,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				1, // Table Index.
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.fill (externref) - type mismatch",
@@ -2470,7 +2471,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				0, // Table of funcref type -> mismatch.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.fill: type mismatch: expected funcref, but was externref`,
 		},
 		{
@@ -2480,7 +2481,7 @@ func TestModule_funcValidation_TableGrowSizeFill(t *testing.T) {
 				10, // Table Index.
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `table of index 10 not found`,
 		},
 	}
@@ -2508,7 +2509,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 	tests := []struct {
 		name        string
 		body        []byte
-		flag        Features
+		flag        api.CoreFeatures
 		expectedErr string
 	}{
 		{
@@ -2520,7 +2521,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.get (externref)",
@@ -2531,7 +2532,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.get (disabled)",
@@ -2541,7 +2542,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag:        Features20191205,
+			flag:        api.CoreFeaturesV1,
 			expectedErr: `table.get is invalid as feature "reference-types" is disabled`,
 		},
 		{
@@ -2552,7 +2553,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeTableSet, 0,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.set type mismatch (src=funcref, dst=externref)",
@@ -2562,7 +2563,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeTableSet, 1,
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.set: type mismatch: expected externref, but was funcref`,
 		},
 		{
@@ -2573,7 +2574,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeTableSet, 1,
 				OpcodeEnd,
 			},
-			flag: FeatureReferenceTypes,
+			flag: api.CoreFeatureReferenceTypes,
 		},
 		{
 			name: "table.set type mismatch (src=externref, dst=funcref)",
@@ -2583,7 +2584,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeTableSet, 0,
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `cannot pop the operand for table.set: type mismatch: expected funcref, but was externref`,
 		},
 		{
@@ -2592,7 +2593,7 @@ func TestModule_funcValidation_TableGetSet(t *testing.T) {
 				OpcodeTableSet, 1,
 				OpcodeEnd,
 			},
-			flag:        Features20191205,
+			flag:        api.CoreFeaturesV1,
 			expectedErr: `table.set is invalid as feature "reference-types" is disabled`,
 		},
 	}
@@ -2619,7 +2620,7 @@ func TestModule_funcValidation_Select_error(t *testing.T) {
 	tests := []struct {
 		name        string
 		body        []byte
-		flag        Features
+		flag        api.CoreFeatures
 		expectedErr string
 	}{
 		{
@@ -2630,7 +2631,7 @@ func TestModule_funcValidation_Select_error(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag:        Features20191205,
+			flag:        api.CoreFeaturesV1,
 			expectedErr: "typed_select is invalid as feature \"reference-types\" is disabled",
 		},
 		{
@@ -2639,7 +2640,7 @@ func TestModule_funcValidation_Select_error(t *testing.T) {
 				OpcodeI32Const, 0, OpcodeI32Const, 0, OpcodeI32Const, 0,
 				OpcodeTypedSelect, 2, // immediate vector's size must be one
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `too many type immediates for typed_select`,
 		},
 		{
@@ -2649,7 +2650,7 @@ func TestModule_funcValidation_Select_error(t *testing.T) {
 				OpcodeTypedSelect, 1, 0,
 				OpcodeEnd,
 			},
-			flag:        FeatureReferenceTypes,
+			flag:        api.CoreFeatureReferenceTypes,
 			expectedErr: `invalid type unknown for typed_select`,
 		},
 	}
@@ -3147,7 +3148,7 @@ func TestModule_funcValidation_SIMD(t *testing.T) {
 				FunctionSection: []Index{0},
 				CodeSection:     []*Code{{Body: tc.body}},
 			}
-			err := m.validateFunction(FeatureSIMD, 0, []Index{0}, nil, &Memory{}, nil, nil)
+			err := m.validateFunction(api.CoreFeatureSIMD, 0, []Index{0}, nil, &Memory{}, nil, nil)
 			require.NoError(t, err)
 		})
 	}
@@ -3157,7 +3158,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 	type testCase struct {
 		name        string
 		body        []byte
-		flag        Features
+		flag        api.CoreFeatures
 		expectedErr string
 	}
 
@@ -3168,7 +3169,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 				OpcodeVecPrefix,
 				OpcodeVecF32x4Abs,
 			},
-			flag:        Features20191205,
+			flag:        api.CoreFeaturesV1,
 			expectedErr: "f32x4.abs invalid as feature \"simd\" is disabled",
 		},
 		{
@@ -3179,7 +3180,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 				1, 1, 1, 1, 1, 1, 1, 1,
 				1, 1, 1, 1, 1, 1, 1,
 			},
-			flag:        FeatureSIMD,
+			flag:        api.CoreFeatureSIMD,
 			expectedErr: "cannot read constant vector value for v128.const",
 		},
 		{
@@ -3194,7 +3195,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag:        FeatureSIMD,
+			flag:        api.CoreFeatureSIMD,
 			expectedErr: "cannot pop the operand for i32x4.add: v128 missing",
 		},
 		{
@@ -3209,12 +3210,12 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 				OpcodeDrop,
 				OpcodeEnd,
 			},
-			flag:        FeatureSIMD,
+			flag:        api.CoreFeatureSIMD,
 			expectedErr: "cannot pop the operand for i64x2.add: v128 missing",
 		},
 		{
 			name: "shuffle lane index not found",
-			flag: FeatureSIMD,
+			flag: api.CoreFeatureSIMD,
 			body: []byte{
 				OpcodeVecPrefix,
 				OpcodeVecV128i8x16Shuffle,
@@ -3223,7 +3224,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 		},
 		{
 			name: "shuffle lane index not found",
-			flag: FeatureSIMD,
+			flag: api.CoreFeatureSIMD,
 			body: []byte{
 				OpcodeVecPrefix,
 				OpcodeVecV128i8x16Shuffle,
@@ -3238,7 +3239,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 		n := VectorInstructionName(op)
 		tests = append(tests, testCase{
 			name: n + "/lane index out of range",
-			flag: FeatureSIMD,
+			flag: api.CoreFeatureSIMD,
 			body: []byte{
 				OpcodeVecPrefix, op, lane,
 			},
@@ -3265,7 +3266,7 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 		n := VectorInstructionName(op)
 		tests = append(tests, testCase{
 			name: n + "/lane index out of range",
-			flag: FeatureSIMD,
+			flag: api.CoreFeatureSIMD,
 			body: []byte{
 				OpcodeVecPrefix, op,
 				0, 0, // align and offset.
@@ -3317,7 +3318,7 @@ func TestDecodeBlockType(t *testing.T) {
 		} {
 			tc := tc
 			t.Run(tc.name, func(t *testing.T) {
-				actual, read, err := DecodeBlockType(nil, bytes.NewReader([]byte{tc.in}), Features20220419)
+				actual, read, err := DecodeBlockType(nil, bytes.NewReader([]byte{tc.in}), api.CoreFeaturesV2)
 				require.NoError(t, err)
 				require.Equal(t, uint64(1), read)
 				require.Equal(t, 0, len(actual.Params))
@@ -3341,7 +3342,7 @@ func TestDecodeBlockType(t *testing.T) {
 			{Params: []ValueType{ValueTypeF32, ValueTypeV128}, Results: []ValueType{ValueTypeI32, ValueTypeF32, ValueTypeV128}},
 		}
 		for index, expected := range types {
-			actual, read, err := DecodeBlockType(types, bytes.NewReader([]byte{byte(index)}), FeatureMultiValue)
+			actual, read, err := DecodeBlockType(types, bytes.NewReader([]byte{byte(index)}), api.CoreFeatureMultiValue)
 			require.NoError(t, err)
 			require.Equal(t, uint64(1), read)
 			require.Equal(t, expected, actual)
@@ -3406,7 +3407,7 @@ func TestFuncValidation_UnreachableBrTable_NotModifyTypes(t *testing.T) {
 	} {
 		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
-			err := tc.m.validateFunction(Features20220419, 0, nil, nil, nil, nil, nil)
+			err := tc.m.validateFunction(api.CoreFeaturesV2, 0, nil, nil, nil, nil, nil)
 			require.NoError(t, err)
 
 			// Ensures that funcType has remained intact.
@@ -3530,7 +3531,7 @@ func TestModule_funcValidation_loopWithParams(t *testing.T) {
 				FunctionSection: []Index{0},
 				CodeSection:     []*Code{{Body: tc.body}},
 			}
-			err := m.validateFunction(FeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
+			err := m.validateFunction(api.CoreFeatureMultiValue, 0, []Index{0}, nil, nil, nil, nil)
 			if tc.expErr != "" {
 				require.EqualError(t, err, tc.expErr)
 			} else {
diff --git a/internal/wasm/host_test.go b/internal/wasm/host_test.go
index 43a63afb..3a406ce7 100644
--- a/internal/wasm/host_test.go
+++ b/internal/wasm/host_test.go
@@ -175,7 +175,8 @@ func TestNewHostModule(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal, Features20191205|FeatureMultiValue)
+			m, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal,
+				api.CoreFeaturesV1|api.CoreFeatureMultiValue)
 			require.NoError(t, e)
 			requireHostModuleEquals(t, tc.expected, m)
 		})
@@ -258,7 +259,7 @@ func TestNewHostModule_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal, Features20191205)
+			_, e := NewHostModule(tc.moduleName, tc.nameToGoFunc, nil, tc.nameToMemory, tc.nameToGlobal, api.CoreFeaturesV1)
 			require.EqualError(t, e, tc.expectedErr)
 		})
 	}
diff --git a/internal/wasm/module_test.go b/internal/wasm/module_test.go
index ef10495a..c75e5756 100644
--- a/internal/wasm/module_test.go
+++ b/internal/wasm/module_test.go
@@ -346,7 +346,7 @@ func TestModule_Validate_Errors(t *testing.T) {
 		tc := tt
 
 		t.Run(tc.name, func(t *testing.T) {
-			err := tc.input.Validate(Features20191205)
+			err := tc.input.Validate(api.CoreFeaturesV1)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
@@ -457,12 +457,12 @@ func TestModule_validateFunctions(t *testing.T) {
 			FunctionSection: []uint32{0},
 			CodeSection:     []*Code{{Body: []byte{OpcodeI32Const, 0, OpcodeDrop, OpcodeEnd}}},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.NoError(t, err)
 	})
 	t.Run("too many functions", func(t *testing.T) {
 		m := Module{}
-		err := m.validateFunctions(Features20191205, []uint32{1, 2, 3, 4}, nil, nil, nil, 3)
+		err := m.validateFunctions(api.CoreFeaturesV1, []uint32{1, 2, 3, 4}, nil, nil, nil, 3)
 		require.Error(t, err)
 		require.EqualError(t, err, "too many functions in a store")
 	})
@@ -472,7 +472,7 @@ func TestModule_validateFunctions(t *testing.T) {
 			FunctionSection: []Index{0},
 			CodeSection:     nil,
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.EqualError(t, err, "code count (0) != function count (1)")
 	})
@@ -482,7 +482,7 @@ func TestModule_validateFunctions(t *testing.T) {
 			FunctionSection: []Index{1},
 			CodeSection:     []*Code{{Body: []byte{OpcodeEnd}}},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.EqualError(t, err, "invalid function[0]: type section index 1 out of range")
 	})
@@ -492,7 +492,7 @@ func TestModule_validateFunctions(t *testing.T) {
 			FunctionSection: []Index{0},
 			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.Contains(t, err.Error(), "invalid function[0]: cannot pop the 1st f32 operand")
 	})
@@ -503,7 +503,7 @@ func TestModule_validateFunctions(t *testing.T) {
 			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
 			ExportSection:   []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 0}},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
 	})
@@ -515,7 +515,7 @@ func TestModule_validateFunctions(t *testing.T) {
 			CodeSection:     []*Code{{Body: []byte{OpcodeF32Abs}}},
 			ExportSection:   []*Export{{Name: "f1", Type: ExternTypeFunc, Index: 1}},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.Contains(t, err.Error(), `invalid function[0] export["f1"]: cannot pop the 1st f32`)
 	})
@@ -529,7 +529,7 @@ func TestModule_validateFunctions(t *testing.T) {
 				{Name: "f2", Type: ExternTypeFunc, Index: 0},
 			},
 		}
-		err := m.validateFunctions(Features20191205, nil, nil, nil, nil, MaximumFunctionIndex)
+		err := m.validateFunctions(api.CoreFeaturesV1, nil, nil, nil, nil, MaximumFunctionIndex)
 		require.Error(t, err)
 		require.Contains(t, err.Error(), `invalid function[0] export["f1","f2"]: cannot pop the 1st f32`)
 	})
@@ -538,7 +538,7 @@ func TestModule_validateFunctions(t *testing.T) {
 func TestModule_validateMemory(t *testing.T) {
 	t.Run("active data segment exits but memory not declared", func(t *testing.T) {
 		m := Module{DataSection: []*DataSegment{{OffsetExpression: &ConstantExpression{}}}}
-		err := m.validateMemory(nil, nil, Features20191205)
+		err := m.validateMemory(nil, nil, api.CoreFeaturesV1)
 		require.Error(t, err)
 		require.Contains(t, "unknown memory", err.Error())
 	})
@@ -548,7 +548,7 @@ func TestModule_validateMemory(t *testing.T) {
 				Opcode: OpcodeUnreachable, // Invalid!
 			},
 		}}}
-		err := m.validateMemory(&Memory{}, nil, Features20191205)
+		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
 		require.EqualError(t, err, "calculate offset: invalid opcode for const expression: 0x0")
 	})
 	t.Run("ok", func(t *testing.T) {
@@ -559,7 +559,7 @@ func TestModule_validateMemory(t *testing.T) {
 				Data:   leb128.EncodeInt32(1),
 			},
 		}}}
-		err := m.validateMemory(&Memory{}, nil, Features20191205)
+		err := m.validateMemory(&Memory{}, nil, api.CoreFeaturesV1)
 		require.NoError(t, err)
 	})
 }
@@ -567,19 +567,19 @@ func TestModule_validateMemory(t *testing.T) {
 func TestModule_validateImports(t *testing.T) {
 	tests := []struct {
 		name            string
-		enabledFeatures Features
+		enabledFeatures api.CoreFeatures
 		i               *Import
 		expectedErr     string
 	}{
 		{name: "empty import section"},
 		{
 			name:            "func",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			i:               &Import{Module: "m", Name: "n", Type: ExternTypeFunc, DescFunc: 0},
 		},
 		{
 			name:            "global var disabled",
-			enabledFeatures: Features20191205.Set(FeatureMutableGlobal, false),
+			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
 			i: &Import{
 				Module:     "m",
 				Name:       "n",
@@ -590,7 +590,7 @@ func TestModule_validateImports(t *testing.T) {
 		},
 		{
 			name:            "table",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			i: &Import{
 				Module:    "m",
 				Name:      "n",
@@ -600,7 +600,7 @@ func TestModule_validateImports(t *testing.T) {
 		},
 		{
 			name:            "memory",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			i: &Import{
 				Module:  "m",
 				Name:    "n",
@@ -630,7 +630,7 @@ func TestModule_validateImports(t *testing.T) {
 func TestModule_validateExports(t *testing.T) {
 	tests := []struct {
 		name            string
-		enabledFeatures Features
+		enabledFeatures api.CoreFeatures
 		exportSection   []*Export
 		functions       []Index
 		globals         []*GlobalType
@@ -641,71 +641,71 @@ func TestModule_validateExports(t *testing.T) {
 		{name: "empty export section", exportSection: []*Export{}},
 		{
 			name:            "func",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeFunc, Index: 0}},
 			functions:       []Index{100 /* arbitrary type id*/},
 		},
 		{
 			name:            "func out of range",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeFunc, Index: 1, Name: "e"}},
 			functions:       []Index{100 /* arbitrary type id*/},
 			expectedErr:     `unknown function for export["e"]`,
 		},
 		{
 			name:            "global const",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0}},
 			globals:         []*GlobalType{{ValType: ValueTypeI32}},
 		},
 		{
 			name:            "global var",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0}},
 			globals:         []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
 		},
 		{
 			name:            "global var disabled",
-			enabledFeatures: Features20191205.Set(FeatureMutableGlobal, false),
+			enabledFeatures: api.CoreFeaturesV1.SetEnabled(api.CoreFeatureMutableGlobal, false),
 			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 0, Name: "e"}},
 			globals:         []*GlobalType{{ValType: ValueTypeI32, Mutable: true}},
 			expectedErr:     `invalid export["e"] global[0]: feature "mutable-global" is disabled`,
 		},
 		{
 			name:            "global out of range",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeGlobal, Index: 1, Name: "e"}},
 			globals:         []*GlobalType{{}},
 			expectedErr:     `unknown global for export["e"]`,
 		},
 		{
 			name:            "table",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeTable, Index: 0}},
 			tables:          []*Table{{}},
 		},
 		{
 			name:            "multiple tables",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeTable, Index: 0}, {Type: ExternTypeTable, Index: 1}, {Type: ExternTypeTable, Index: 2}},
 			tables:          []*Table{{}, {}, {}},
 		},
 		{
 			name:            "table out of range",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeTable, Index: 1, Name: "e"}},
 			tables:          []*Table{},
 			expectedErr:     `table for export["e"] out of range`,
 		},
 		{
 			name:            "memory",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeMemory, Index: 0}},
 			memory:          &Memory{},
 		},
 		{
 			name:            "memory out of range",
-			enabledFeatures: Features20191205,
+			enabledFeatures: api.CoreFeaturesV1,
 			exportSection:   []*Export{{Type: ExternTypeMemory, Index: 0, Name: "e"}},
 			tables:          []*Table{},
 			expectedErr:     `memory for export["e"] out of range`,
diff --git a/internal/wasm/store_test.go b/internal/wasm/store_test.go
index 24fd1d37..91575105 100644
--- a/internal/wasm/store_test.go
+++ b/internal/wasm/store_test.go
@@ -91,7 +91,7 @@ func TestModuleInstance_Memory(t *testing.T) {
 
 func TestStore_Instantiate(t *testing.T) {
 	s, ns := newStore()
-	m, err := NewHostModule("", map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, Features20191205)
+	m, err := NewHostModule("", map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
 	require.NoError(t, err)
 
 	sysCtx := sys.DefaultContext(nil)
@@ -169,7 +169,7 @@ func TestStore_CloseWithExitCode(t *testing.T) {
 func TestStore_hammer(t *testing.T) {
 	const importedModuleName = "imported"
 
-	m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, Features20191205)
+	m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
 	require.NoError(t, err)
 
 	s, ns := newStore()
@@ -223,7 +223,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 	const importedModuleName = "imported"
 	const importingModuleName = "test"
 
-	m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, Features20191205)
+	m, err := NewHostModule(importedModuleName, map[string]interface{}{"fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
 	require.NoError(t, err)
 
 	t.Run("Fails if module name already in use", func(t *testing.T) {
@@ -314,7 +314,7 @@ func TestStore_Instantiate_Errors(t *testing.T) {
 }
 
 func TestCallContext_ExportedFunction(t *testing.T) {
-	host, err := NewHostModule("host", map[string]interface{}{"host_fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, Features20191205)
+	host, err := NewHostModule("host", map[string]interface{}{"host_fn": func(api.Module) {}}, nil, map[string]*Memory{}, map[string]*Global{}, api.CoreFeaturesV1)
 	require.NoError(t, err)
 
 	s, ns := newStore()
@@ -358,7 +358,7 @@ type mockCallEngine struct {
 }
 
 func newStore() (*Store, *Namespace) {
-	return NewStore(Features20191205, &mockEngine{shouldCompileFail: false, callFailIndex: -1})
+	return NewStore(api.CoreFeaturesV1, &mockEngine{shouldCompileFail: false, callFailIndex: -1})
 }
 
 // CompileModule implements the same method as documented on wasm.Engine.
diff --git a/internal/wasm/table_test.go b/internal/wasm/table_test.go
index 6404f037..1bc75301 100644
--- a/internal/wasm/table_test.go
+++ b/internal/wasm/table_test.go
@@ -4,6 +4,7 @@ import (
 	"math"
 	"testing"
 
+	"github.com/tetratelabs/wazero/api"
 	"github.com/tetratelabs/wazero/internal/leb128"
 	"github.com/tetratelabs/wazero/internal/testing/require"
 )
@@ -321,13 +322,13 @@ func TestModule_validateTable(t *testing.T) {
 			_, _, _, tables, err := tc.input.AllDeclarations()
 			require.NoError(t, err)
 
-			vt, err := tc.input.validateTable(Features20191205, tables, maxTableIndex)
+			vt, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, vt)
 
 			// Ensure it was cached. We have to use Equal not Same because this is a slice, not a pointer.
 			require.Equal(t, vt, tc.input.validatedActiveElementSegments)
-			vt2, err := tc.input.validateTable(Features20191205, tables, maxTableIndex)
+			vt2, err := tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.NoError(t, err)
 			require.Equal(t, vt, vt2)
 		})
@@ -595,7 +596,7 @@ func TestModule_validateTable_Errors(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			_, _, _, tables, err := tc.input.AllDeclarations()
 			require.NoError(t, err)
-			_, err = tc.input.validateTable(Features20191205, tables, maxTableIndex)
+			_, err = tc.input.validateTable(api.CoreFeaturesV1, tables, maxTableIndex)
 			require.EqualError(t, err, tc.expectedErr)
 		})
 	}
diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go
index 5aa32f9a..cb573cb4 100644
--- a/internal/wazeroir/compiler_test.go
+++ b/internal/wazeroir/compiler_test.go
@@ -38,7 +38,7 @@ func TestCompile(t *testing.T) {
 		name            string
 		module          *wasm.Module
 		expected        *CompilationResult
-		enabledFeatures wasm.Features
+		enabledFeatures api.CoreFeatures
 	}{
 		{
 			name: "nullary",
@@ -227,7 +227,7 @@ func TestCompile(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			enabledFeatures := tc.enabledFeatures
 			if enabledFeatures == 0 {
-				enabledFeatures = wasm.Features20220419
+				enabledFeatures = api.CoreFeaturesV2
 			}
 			for _, tp := range tc.module.TypeSection {
 				tp.CacheNumInUint64()
@@ -253,7 +253,7 @@ func TestCompile_Block(t *testing.T) {
 		name            string
 		module          *wasm.Module
 		expected        *CompilationResult
-		enabledFeatures wasm.Features
+		enabledFeatures api.CoreFeatures
 	}{
 		{
 			name: "type-i32-i32",
@@ -372,7 +372,7 @@ func TestCompile_BulkMemoryOperations(t *testing.T) {
 		TableTypes:       []wasm.RefType{},
 	}
 
-	res, err := CompileFunctions(ctx, wasm.FeatureBulkMemoryOperations, 0, module)
+	res, err := CompileFunctions(ctx, api.CoreFeatureBulkMemoryOperations, 0, module)
 	require.NoError(t, err)
 	require.Equal(t, expected, res[0])
 }
@@ -394,7 +394,7 @@ func TestCompile_MultiValue(t *testing.T) {
 		name            string
 		module          *wasm.Module
 		expected        *CompilationResult
-		enabledFeatures wasm.Features
+		enabledFeatures api.CoreFeatures
 	}{
 		{
 			name: "swap",
@@ -663,7 +663,7 @@ func TestCompile_MultiValue(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			enabledFeatures := tc.enabledFeatures
 			if enabledFeatures == 0 {
-				enabledFeatures = wasm.Features20220419
+				enabledFeatures = api.CoreFeaturesV2
 			}
 			for _, tp := range tc.module.TypeSection {
 				tp.CacheNumInUint64()
@@ -706,7 +706,7 @@ func TestCompile_NonTrappingFloatToIntConversion(t *testing.T) {
 	for _, tp := range module.TypeSection {
 		tp.CacheNumInUint64()
 	}
-	res, err := CompileFunctions(ctx, wasm.FeatureNonTrappingFloatToIntConversion, 0, module)
+	res, err := CompileFunctions(ctx, api.CoreFeatureNonTrappingFloatToIntConversion, 0, module)
 	require.NoError(t, err)
 	require.Equal(t, expected, res[0])
 }
@@ -737,14 +737,14 @@ func TestCompile_SignExtensionOps(t *testing.T) {
 	for _, tp := range module.TypeSection {
 		tp.CacheNumInUint64()
 	}
-	res, err := CompileFunctions(ctx, wasm.FeatureSignExtensionOps, 0, module)
+	res, err := CompileFunctions(ctx, api.CoreFeatureSignExtensionOps, 0, module)
 	require.NoError(t, err)
 	require.Equal(t, expected, res[0])
 }
 
-func requireCompilationResult(t *testing.T, enabledFeatures wasm.Features, expected *CompilationResult, module *wasm.Module) {
+func requireCompilationResult(t *testing.T, enabledFeatures api.CoreFeatures, expected *CompilationResult, module *wasm.Module) {
 	if enabledFeatures == 0 {
-		enabledFeatures = wasm.Features20220419
+		enabledFeatures = api.CoreFeaturesV2
 	}
 	res, err := CompileFunctions(ctx, enabledFeatures, 0, module)
 	require.NoError(t, err)
@@ -785,7 +785,7 @@ func TestCompile_CallIndirectNonZeroTableIndex(t *testing.T) {
 		Types: []*wasm.FunctionType{v_v, v_v, v_v},
 	}
 
-	res, err := CompileFunctions(ctx, wasm.FeatureBulkMemoryOperations, 0, module)
+	res, err := CompileFunctions(ctx, api.CoreFeatureBulkMemoryOperations, 0, module)
 	require.NoError(t, err)
 	require.Equal(t, expected, res[0])
 }
@@ -875,7 +875,7 @@ func TestCompile_Refs(t *testing.T) {
 				FunctionSection: []wasm.Index{0},
 				CodeSection:     []*wasm.Code{{Body: tc.body}},
 			}
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, module)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, module)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 		})
@@ -944,7 +944,7 @@ func TestCompile_TableGetOrSet(t *testing.T) {
 				CodeSection:     []*wasm.Code{{Body: tc.body}},
 				TableSection:    []*wasm.Table{{}},
 			}
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, module)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, module)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 		})
@@ -1013,7 +1013,7 @@ func TestCompile_TableGrowFillSize(t *testing.T) {
 				CodeSection:     []*wasm.Code{{Body: tc.body}},
 				TableSection:    []*wasm.Table{{}},
 			}
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, module)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, module)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 			require.True(t, res[0].HasTable)
@@ -1221,7 +1221,7 @@ func TestCompile_Locals(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, tc.mod)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, tc.mod)
 			require.NoError(t, err)
 			msg := fmt.Sprintf("\nhave:\n\t%s\nwant:\n\t%s", Format(res[0].Operations), Format(tc.expected))
 			require.Equal(t, tc.expected, res[0].Operations, msg)
@@ -2567,7 +2567,7 @@ func TestCompile_Vec(t *testing.T) {
 				MemorySection:   &wasm.Memory{},
 				CodeSection:     []*wasm.Code{{Body: tc.body}},
 			}
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, module)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, module)
 			require.NoError(t, err)
 
 			var actual Operation
@@ -2644,7 +2644,7 @@ func TestCompile_unreachable_Br_BrIf_BrTable(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, tc.mod)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, tc.mod)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 		})
@@ -2684,7 +2684,7 @@ func TestCompile_drop_vectors(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, tc.mod)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, tc.mod)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 		})
@@ -2754,7 +2754,7 @@ func TestCompile_select_vectors(t *testing.T) {
 	for _, tt := range tests {
 		tc := tt
 		t.Run(tc.name, func(t *testing.T) {
-			res, err := CompileFunctions(ctx, wasm.Features20220419, 0, tc.mod)
+			res, err := CompileFunctions(ctx, api.CoreFeaturesV2, 0, tc.mod)
 			require.NoError(t, err)
 			require.Equal(t, tc.expected, res[0].Operations)
 		})
diff --git a/runtime_test.go b/runtime_test.go
index 22b41424..3b0c9e18 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -24,7 +24,7 @@ var (
 func TestNewRuntimeWithConfig_version(t *testing.T) {
 	cfg := NewRuntimeConfig().(*runtimeConfig)
 	oldNewEngine := cfg.newEngine
-	cfg.newEngine = func(ctx context.Context, features wasm.Features) wasm.Engine {
+	cfg.newEngine = func(ctx context.Context, features api.CoreFeatures) wasm.Engine {
 		// Ensures that wazeroVersion is propagated to the engine.
 		v := ctx.Value(version.WazeroVersionKey{})
 		require.NotNil(t, v)
@@ -519,7 +519,7 @@ func TestRuntime_CloseWithExitCode(t *testing.T) {
 func TestRuntime_Close_ClosesCompiledModules(t *testing.T) {
 	engine := &mockEngine{name: "mock", cachedModules: map[*wasm.Module]struct{}{}}
 	conf := *engineLessConfig
-	conf.newEngine = func(context.Context, wasm.Features) wasm.Engine {
+	conf.newEngine = func(context.Context, api.CoreFeatures) wasm.Engine {
 		return engine
 	}
 	r := NewRuntimeWithConfig(testCtx, &conf)