# How to Securely Manage Sensitive Credentials in PicList-Core Configurations

> Securely manage PicList-Core credentials using environment variables CLI masking and isolate secrets. Keep configurations out of version control for robust security. Learn best practices now.

- Repository: [Kuingsmile/piclist-core](https://github.com/kuingsmile/piclist-core)
- Tags: best-practices
- Published: 2026-03-05

---

**Use environment variables via `.env` files for secrets, leverage built-in CLI masking, and isolate credentials per uploader while keeping configuration files out of version control.**

PicList-Core handles image uploads across multiple cloud platforms, requiring secure storage of API tokens, secret keys, and passwords. Understanding how to securely manage sensitive credentials in PicList-Core configurations is essential for protecting your cloud storage accounts and maintaining operational security. The codebase implements specific safeguards in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts) and [`src/utils/runScripts.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/runScripts.ts) that you can leverage alongside operational best practices.

## Environment-Based Secret Injection

The most secure way to handle credentials is to keep them out of the configuration JSON entirely. PicList-Core supports this through automatic `.env` file loading.

In [`src/utils/runScripts.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/runScripts.ts), the `getFreshEnv` function reads a `.env` file at runtime and merges its variables into `process.env`:

```typescript
// src/utils/runScripts.ts (excerpt)
import dotenv from 'dotenv';
function getFreshEnv(envPath: string): Record<string, string> {
  if (fs.existsSync(envPath)) {
    const buf = fs.readFileSync(envPath);
    const config = dotenv.parse(buf);
    // Merge into process.env – makes credentials available to uploaders
    for (const k in config) process.env[k] = config[k];
    return config;
  }
  return {};
}

```

**Best practice:** Create a `.env` file in your project root (ensure it is git-ignored) containing your secrets:

```dotenv
GITHUB_TOKEN=ghp_XXXXXXXXXXXXXXXXXXXX
SMMS_TOKEN=your_smms_token
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...

```

Launch PicList-Core using `cross-env` or allow [`runScripts.ts`](https://github.com/kuingsmile/piclist-core/blob/main/runScripts.ts) to load the `.env` automatically. The uploader modules will read these values from `process.env` without ever persisting them to disk in the JSON configuration.

## CLI Masking and Display Protection

When viewing configurations via the command line, PicList-Core automatically masks sensitive fields to prevent accidental exposure.

In [`src/plugins/commander/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/plugins/commander/configManager.ts), the `config-show` command implements logic that detects credential-related keys:

```typescript
// src/plugins/commander/configManager.ts (excerpt)
const displayValue =
  typeof value === 'string' &&
  (key.includes('password') ||
   key.includes('token') ||
   key.includes('key'))
    ? '***'                     // secret hidden
    : JSON.stringify(value);
ctx.log.info(`  ${key}: ${displayValue}`);

```

**Best practice:** Always use the official CLI commands (`picgo config-show`, `piclist config-show`) rather than manually `cat`-ing the configuration file. If you need to debug, avoid `console.log` statements that print the entire config object; instead, log specific non-sensitive fields.

## Per-Uploader Credential Isolation

PicList-Core enforces separation of concerns by defining distinct TypeScript interfaces for each uploader's credentials in [`src/types/index.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/types/index.ts).

Each uploader has its own configuration structure:

- `IGithubConfig` contains `token`, `repo`, `branch`
- `ISmmsConfig` contains `token`
- `IAwsS3PListUserConfig` contains `accessKeyID`, `secretAccessKey`, `sessionToken`

This isolation ensures that credentials for one service cannot accidentally leak into another uploader's configuration object.

**Best practice:** When adding new uploaders or modifying existing ones, maintain this namespace isolation. Rotate credentials for one uploader without affecting others, and use the specific config interfaces to validate that only expected fields are present.

## Multi-Config Rotation and Management

PicList-Core supports multiple configuration profiles per uploader, allowing you to rotate credentials without downtime.

In [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts), the `syncConfigToPicBed` method handles the migration between the legacy single-config format and the new multi-config structure (`uploader.{name}.configList`):

```typescript
// src/utils/configManager.ts (excerpt)
syncConfigToPicBed() {
  // When a config becomes the default, it syncs to legacy picBed location
  // but remains isolated in the configList array
}

```

**Best practice:** Use the `config-use` command to switch between credential sets rather than editing JSON manually. When a token expires or is compromised, add a new configuration entry with fresh credentials using `config-add` (or the equivalent API), then activate it with `config-use <uploader> <config-name>`. This leaves the old credential in place (for rollback if needed) while immediately switching to the secure replacement.

## Plugin Security Boundaries

Third-party plugins operate within a restricted context that prevents unauthorized modification of your credentials.

The `IPicGo` interface in [`src/types/index.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/types/index.ts) exposes `ctx.getConfig<T>()` as read-only for general plugin use. Write access is restricted to `ctx.saveConfig()`, which validates against the internal schema:

```typescript
// src/types/index.ts (excerpt)
interface IPicGo {
  getConfig<T>(name?: string): T | undefined;
  // saveConfig is the only write path, enforcing schema validation
}

```

**Best practice:** Audit third-party plugins before installation. Verify that they only use `ctx.getConfig()` to read necessary settings and do not attempt to manipulate the configuration object directly. Prefer plugins from the official PicList-Core ecosystem that have been reviewed for security compliance.

## File System Permissions and Version Control

Operational security extends beyond the codebase to how you store configuration files on disk.

**Best practice:**
- **Git-ignore your config:** Never commit [`config.json`](https://github.com/kuingsmile/piclist-core/blob/main/config.json) or `.env` files to version control. Add them to `.gitignore` immediately.
- **Restrict file permissions:** Set your configuration directory to `chmod 700` and the config file to `chmod 600` (read/write for owner only) on Unix systems.
- **Use OS-specific vaults:** On macOS, consider storing secrets in Keychain and referencing them via environment variables. On Windows, use Credential Manager or Windows Environment Variables.
- **Separate concerns:** Keep PicList-Core's configuration in `$HOME/.picgo` (the default) rather than project directories to avoid accidental exposure in shared codebases.

## Summary

- **Use `.env` files** loaded by [`src/utils/runScripts.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/runScripts.ts) to inject secrets into `process.env` without writing them to JSON configuration files.
- **Rely on built-in masking** in [`src/plugins/commander/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/plugins/commander/configManager.ts) to prevent credential exposure in CLI output.
- **Isolate credentials** per uploader using the typed interfaces in [`src/types/index.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/types/index.ts) to prevent cross-contamination.
- **Rotate credentials safely** using the multi-config support in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts) rather than manual JSON editing.
- **Restrict plugin access** by auditing third-party code that uses `ctx.getConfig()` and ensuring write operations only occur through `ctx.saveConfig()`.
- **Protect files on disk** with strict permissions (600/700) and git-ignore rules to prevent accidental version control commits.

## Frequently Asked Questions

### How do I prevent my API tokens from appearing in the PicList-Core configuration file?

Store your secrets in a `.env` file in your project root. The [`runScripts.ts`](https://github.com/kuingsmile/piclist-core/blob/main/runScripts.ts) utility automatically parses this file and merges values into `process.env` at runtime, allowing uploaders to read tokens from environment variables rather than the JSON configuration. Ensure `.env` is listed in `.gitignore` and set file permissions to `600` to restrict access.

### Can I switch between multiple sets of credentials for the same uploader without editing the config file manually?

Yes. PicList-Core supports multi-configuration profiles per uploader through the `configList` structure managed in [`src/utils/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/utils/configManager.ts). Use the `config-use <uploader> <config-name>` command to activate a specific credential set. This updates the active configuration without requiring manual JSON edits and allows instant rollback to previous credentials if needed.

### Are third-party plugins able to read or steal my stored credentials?

Plugins receive a read-only context (`ctx`) that exposes `getConfig()` but restricts write access to the `saveConfig()` API, which validates against the internal schema. While plugins can theoretically read configuration values, they cannot modify the config file directly. Always audit third-party plugin code before installation to ensure it only accesses necessary configuration keys and does not transmit data externally.

### What fields are automatically masked when displaying configuration via the CLI?

The `config-show` command in [`src/plugins/commander/configManager.ts`](https://github.com/kuingsmile/piclist-core/blob/main/src/plugins/commander/configManager.ts) automatically masks values for any key containing the substrings `password`, `token`, or `key`, replacing the actual value with `***`. This prevents accidental exposure of credentials in terminal output, logs, or screenshots. Always use official CLI commands rather than `cat` or text editors to inspect active configurations.