Skip to content

Multi-Language Support ​

Envi is language-agnostic and works with projects written in any programming language when installed globally. The tool automatically detects package names from various manifest files to organize your environment configurations.

Supported Languages ​

Envi automatically detects package names from the following languages and frameworks:

Language/FrameworkManifest FileExample Package Name
JavaScript/TypeScriptpackage.json@org/app or myapp
RustCargo.tomlrust-app
Gogo.modgo-project
Pythonpyproject.tomlpython-package
PHPcomposer.jsonvendor/package
Dart/Flutterpubspec.yamlflutter_app
Kotlin/Androidsettings.gradle.ktskotlin-app
Java (Gradle)settings.gradlejava-app
Java (Maven)pom.xmlmaven-app

How It Works ​

Detection Order ​

When you run envi capture, Envi checks for manifest files in your project root in the following order:

  1. package.json (JavaScript/TypeScript)
  2. Cargo.toml (Rust)
  3. go.mod (Go)
  4. pyproject.toml (Python)
  5. composer.json (PHP)
  6. pubspec.yaml (Dart/Flutter)
  7. settings.gradle.kts (Kotlin)
  8. settings.gradle (Java/Gradle)
  9. pom.xml (Java/Maven)

The first file found is used to extract the package name. If no manifest file is found, Envi uses the folder name as a fallback.

Storage Organization ​

Configurations are stored in ~/.envi/store/ with filenames based on the detected package name:

  • Scoped packages: ~/.envi/store/@org/package.maml
  • Unscoped packages: ~/.envi/store/package-name.maml
  • No manifest file: ~/.envi/store/folder-name.maml

Language-Specific Examples ​

JavaScript/TypeScript ​

File: package.json

json
{
  "name": "@myorg/frontend",
  "version": "1.0.0"
}

Detected name: @myorg/frontendStorage path: ~/.envi/store/@myorg/frontend.maml


Rust ​

File: Cargo.toml

toml
[package]
name = "my-rust-app"
version = "0.1.0"
edition = "2021"

Detected name: my-rust-appStorage path: ~/.envi/store/my-rust-app.maml


Go ​

File: go.mod

module github.com/username/awesome-project

go 1.21

Detected name: awesome-project (last segment of module path) Storage path: ~/.envi/store/awesome-project.maml


Python ​

File: pyproject.toml

toml
[project]
name = "data-processor"
version = "1.0.0"

Detected name: data-processorStorage path: ~/.envi/store/data-processor.maml


PHP ​

File: composer.json

json
{
  "name": "vendor/php-library",
  "description": "A PHP library"
}

Detected name: vendor/php-libraryStorage path: ~/.envi/store/vendor/php-library.maml


Dart/Flutter ​

File: pubspec.yaml

yaml
name: my_flutter_app
description: A Flutter application
version: 1.0.0

Detected name: my_flutter_appStorage path: ~/.envi/store/my_flutter_app.maml


Kotlin/Android ​

File: settings.gradle.kts

kotlin
rootProject.name = "android-app"

Detected name: android-appStorage path: ~/.envi/store/android-app.maml


Java (Gradle) ​

File: settings.gradle

groovy
rootProject.name = 'spring-boot-api'

Detected name: spring-boot-apiStorage path: ~/.envi/store/spring-boot-api.maml


Java (Maven) ​

File: pom.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <groupId>com.example</groupId>
  <artifactId>spring-service</artifactId>
  <version>1.0.0</version>
</project>

Detected name: spring-service (uses artifactId) Storage path: ~/.envi/store/spring-service.maml

Configuration ​

Viewing Current Configuration ​

Check which manifest files Envi is configured to detect:

bash
# View your config file
cat ~/.envi/config.maml

Built-in default manifest files (checked in this order):

  1. package.json - JavaScript/TypeScript
  2. Cargo.toml - Rust
  3. go.mod - Go
  4. pyproject.toml - Python
  5. composer.json - PHP
  6. pubspec.yaml - Dart/Flutter
  7. settings.gradle.kts - Kotlin/Android
  8. settings.gradle - Java/Gradle
  9. pom.xml - Java/Maven

Managing Manifest Files ​

By default, Envi checks all supported manifest files in priority order. You can customize this list using CLI commands:

List current manifest files:

bash
envi config manifest_files list

Add a custom manifest file:

bash
envi config manifest_files add my-custom-manifest.json

Remove a manifest file (including defaults):

bash
envi config manifest_files remove pom.xml

Example output:

bash
$ envi config manifest_files list
Manifest files (in priority order):
  • package.json
  • Cargo.toml
  • go.mod
  • pyproject.toml
  • composer.json
  • pubspec.yaml
  • settings.gradle.kts
  • settings.gradle
  • pom.xml

The configuration is stored in ~/.envi/config.maml:

maml
{
  use_version_control: false
  manifest_files: [
    "package.json"
    "Cargo.toml"
    "go.mod"
    # ... full list of manifest files
  ]
  redacted_variables: ["GITHUB_PAT"]
}

Note:

  • Custom manifest files need built-in extractors to work for package name detection
  • All manifest files (including custom ones) can be used for encryption
  • You have full control - you can even remove default files if needed

Encryption Support ​

All supported manifest files can be used for automatic blob encryption, not just package.json!

How It Works ​

When you run envi pack or envi unpack, Envi:

  1. Checks for any supported manifest file in your repository root (in priority order)
  2. Generates an MD5 hash of the manifest file contents
  3. Uses that hash as the encryption/decryption key
  4. No manual secret needed if you have a supported manifest file

Supported Languages for Auto-Encryption ​

LanguageManifest FileStatus
JavaScript/TypeScriptpackage.json✅ Fully supported
RustCargo.toml✅ Fully supported
Gogo.mod✅ Fully supported
Pythonpyproject.toml✅ Fully supported
PHPcomposer.json✅ Fully supported
Dart/Flutterpubspec.yaml✅ Fully supported
Kotlinsettings.gradle.kts✅ Fully supported
Java (Gradle)settings.gradle✅ Fully supported
Java (Maven)pom.xml✅ Fully supported

Example Workflows ​

Rust Project:

bash
# Team member A creates encrypted blob (has Cargo.toml)
envi pack
# Output: Using Cargo.toml for encryption key
# Blob copied to clipboard

# Team member B decrypts blob (has same Cargo.toml)
envi unpack
# Output: Found Cargo.toml - attempting decryption
# ✔ Decryption successful using Cargo.toml

Go Project:

bash
# Team member creates blob (has go.mod)
envi pack
# Output: Using go.mod for encryption key

# Colleague decrypts (has same go.mod)
envi unpack
# Output: Found go.mod - attempting decryption
# ✔ Decryption successful using go.mod

Python Project:

bash
# Create blob with pyproject.toml
envi pack
# Output: Using pyproject.toml for encryption key

# Decrypt with same pyproject.toml
envi unpack
# Output: Found pyproject.toml - attempting decryption
# ✔ Decryption successful using pyproject.toml

Important Notes ​

  • Identical files required: The manifest file must be byte-for-byte identical for decryption to work
  • Any change breaks decryption: Even whitespace or formatting changes will cause decryption to fail
  • MD5 hash: The entire file contents are hashed with MD5 to create a consistent encryption key
  • Fallback to custom secret: If no manifest found or decryption fails, you'll be prompted for a custom secret
  • Custom manifest files: You can add your own manifest files using envi config manifest_files add <filename> (see Managing Manifest Files above). All manifest files can be used for encryption.

Security Implications ​

The same security considerations apply to all manifest-based encryption:

⚠️ WARNING: Blobs encrypted with manifest files are only secure while your codebase remains private. Anyone with access to your repository can decrypt your blobs.

For production secrets: Use a custom secret instead of manifest-based encryption. See Pack for details.

Programmatic Usage ​

Using the API ​

typescript
import {
  getPackageName,
  DEFAULT_MANIFEST_FILES,
  PACKAGE_EXTRACTORS,
} from "@codecompose/envi";

// Get package name from a repository
const packageName = getPackageName("/path/to/rust/project");
console.log(packageName); // "my-rust-app"

// Check default manifest files
console.log(DEFAULT_MANIFEST_FILES);
// ["package.json", "Cargo.toml", "go.mod", ...]

// Access extractors directly
for (const extractor of PACKAGE_EXTRACTORS) {
  const name = extractor.extract("/path/to/project");
  if (name) {
    console.log(`Found ${name} from ${extractor.filename}`);
    break;
  }
}

Custom Extractor ​

typescript
import type { PackageExtractor } from "@codecompose/envi";

const customExtractor: PackageExtractor = {
  filename: "my-manifest.json",
  extract: (repoPath) => {
    // Your custom extraction logic
    return "custom-package-name";
  },
};

Troubleshooting ​

Package Name Not Detected ​

Problem: Envi uses folder name instead of package name from manifest file.

Solution:

  1. Verify manifest file exists in project root:

    bash
    ls -la package.json Cargo.toml go.mod
  2. Check if file is valid:

    bash
    # For JSON files
    cat package.json | jq .
    
    # For TOML files
    cat Cargo.toml
  3. Verify the package name field exists:

    • package.json: name field
    • Cargo.toml: [package] section with name
    • go.mod: First line starting with module
    • pyproject.toml: [project] section with name

Wrong Language Detected ​

Problem: Envi detects the wrong manifest file because your project has multiple manifest files.

Solution: The default detection order prioritizes package.json first, then Cargo.toml, go.mod, etc. If you need different behavior, you have a few options:

  1. Accept the default - The first detected manifest file will be used
  2. Rename manifest files - Temporarily remove or rename unwanted manifest files during capture
  3. Use folder name - If no manifest file is found, Envi uses the folder name as the storage key

Note: You can fully customize the manifest file list using envi config manifest_files commands. Add custom files, remove defaults, or reorder as needed.

Monorepo with Multiple Languages ​

Problem: Monorepo root has manifest files for multiple languages.

Solution:

  1. Option A: Use the most important language first in config
  2. Option B: Remove manifest files from root, keep them in subdirectories
  3. Option C: Accept folder name as storage key (simplest)

Example: TypeScript monorepo with Rust packages:

monorepo/
├── package.json           # Root package.json for workspace
├── packages/
│   ├── frontend/
│   │   ├── .env
│   │   └── package.json
│   └── backend/
│       ├── .env
│       └── Cargo.toml

Running envi capture from root will use the root package.json name for storage. Individual packages can be captured separately by running envi capture from their directories.

Best Practices ​

Global Installation ​

For multi-language support, install Envi globally:

bash
pnpm add -g @codecompose/envi

This allows you to use envi in projects written in any language, not just JavaScript/TypeScript projects.

Configuration Recommendations ​

  1. Use defaults - The built-in manifest files cover all supported languages
  2. Add custom files - Only add custom manifest files using envi config manifest_files add if you have non-standard files
  3. Prioritize custom files - Additional files are checked before defaults, so you can prioritize specific patterns
  4. Use GitHub integration - Sync your custom configuration across machines

Storage Organization ​

  • Scoped packages (e.g., @org/name) automatically create subdirectories
  • Package names with / (e.g., PHP's vendor/package) create subdirectories
  • Simple names create files directly in ~/.envi/store/

Released under the MIT License.