npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@crowi/plugin-storage-aws-s3

v0.1.0-alpha.0

Published

AWS S3 storage driver for Crowi 2.0. Depends on @crowi/plugin-aws for shared credentials.

Readme

@crowi/plugin-storage-aws-s3

AWS S3 storage driver for Crowi 2.0. Stores page attachments and profile pictures in an S3 bucket. Pairs with @crowi/plugin-aws for shared region / access-key configuration.

Install

crowi-admin plugin add @crowi/plugin-storage-aws-s3

(or, in dev: pnpm --filter @crowi/api add -D @crowi/plugin-storage-aws-s3)

The plugin auto-loads its dependency @crowi/plugin-aws. You don't need to add it explicitly.

Configure

1. Activate the driver in crowi.config.json

{
  "plugins": ["@crowi/plugin-storage-aws-s3"],
  "storage": { "driver": "s3" }
}

A server restart is required when storage.driver changes — Crowi reads this file once at boot.

2. Fill in credentials in the admin UI

Open /admin/plugins:

  • @crowi/plugin-awsregion, accessKeyId, secretAccessKey (the secret is encrypted at rest with CROWI_ENCRYPTION_KEY)
  • @crowi/plugin-storage-aws-s3bucket

Leaving accessKeyId and secretAccessKey blank tells the SDK to use its default credential chain (env vars, shared credentials file, EC2 instance role, ECS task role, EKS pod identity, etc). This is the recommended setup when running on AWS — let the platform vend short-lived credentials, don't store long-lived keys in Mongo.

3. Migrate existing files (if switching from local)

crowi-admin storage copy --from local --to s3 --dry-run    # preview
crowi-admin storage copy --from local --to s3              # actual copy

The CLI walks the Attachment collection and User.image URLs, copying every key from one driver to the other. Failures are logged and skipped; re-running is safe (S3 PutObject is overwrite-by-key).

Required IAM permissions

Three S3 operations are called by the driver: PutObject, GetObject, DeleteObject. Signed URLs are produced locally with the SDK presigner, which signs a GetObjectCommand — so the client consuming the signed URL needs GetObject (which the driver's IAM principal already has). ListBucket is not needed.

A minimal IAM policy you can attach to the IAM user / role Crowi runs as:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrowiStorageObjects",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*"
    }
  ]
}

The same policy is shipped at examples/iam-policy.json so you can apply it directly:

aws iam create-policy \
  --policy-name CrowiStorage \
  --policy-document file://examples/iam-policy.json

A variant with s3:ListBucket enabled is at examples/iam-policy-with-list.json — not needed today, but reserved for a future crowi-admin storage list / storage diff command that would enumerate keys server-side.

What does NOT need to be configured

  • Bucket policy — IAM policy is enough for normal single-account setups. Use a bucket policy only if you need cross-account access, SSE enforcement, or public-read overrides — Crowi neither requires nor opposes those, but they're environment-specific and out of scope for this plugin's docs.
  • CORS — Crowi serves signed URLs directly via <img> / download links, which are simple GET requests with no preflight. CORS rules on the bucket are unnecessary unless you add a future feature that uploads from the browser straight to S3 (presigned PUT).
  • SSE-S3 — works out of the box if the bucket has default encryption enabled. The driver does not set ServerSideEncryption explicitly; let the bucket's default policy decide.

Object layout

The driver passes storage keys through to S3 verbatim. The keys Crowi uses are:

attachment/<pageId>/<fileId>/<original-filename>
user/<userId>.<ext>

This matches the v1.x layout, so operators upgrading from Crowi 1.x can point bucket at their existing bucket and files round-trip without migration.

Troubleshooting

| Symptom | Likely cause | |---|---| | bucket=<unset> in the boot log | Set bucket under /admin/plugins for @crowi/plugin-storage-aws-s3. | | region=<default> in the boot log | Set region under /admin/plugins for @crowi/plugin-aws (e.g. ap-northeast-1). | | 403 Forbidden on PUT | Check the IAM policy — s3:PutObject against arn:aws:s3:::<bucket>/*. | | 403 Forbidden on GET via signed URL | Same; the signing principal needs s3:GetObject. | | 301 PermanentRedirect | region mismatch. The bucket lives in a different region than the SDK is configured to talk to. | | Long-lived secret keys committed somewhere | Don't. Use IAM roles on EC2 / ECS / EKS, leave accessKeyId blank. |

See also