August 2026 Floci Update Summary: CloudFront distribution and AppSync GraphQL endpoint are now working

August 2026 Floci Update Summary: CloudFront distribution and AppSync GraphQL endpoint are now working

A summary of Floci, an AWS emulator alternative to LocalStack, covering up to August 2026. We will test and verify the CloudFront distribution and signed URL validation, AppSync GraphQL endpoint, IAM managed policies, and account isolation introduced in versions 1.6.0 and 1.7.0, while also organizing the scope of limitations such as unimplemented resolvers and fixed Allow wildcard behavior.
2026.08.29

This page has been translated by machine translation. View original

Hello. I'm Takeda from the Service Development Division.

We have previously covered "Floci," an open-source AWS emulator that emerged as an alternative to LocalStack.

Another month has passed since last time. This time, I'll summarize the updates from this past month while running some of them locally.

This time, the number of releases decreased to two, while the minor version incremented twice to 1.6.0 and 1.7.0, making each release larger in scope. The released content, the main branch content, and the numbers in the README each move at different times, so this is a month where you need to align which point in time the numbers refer to. I'll also check on AppSync, which was left as homework last time, and follow up on the two issues I filed.

Main changes over the past month (as of August 28, 2026)

Here is a summary of the changes from the previous article (at 1.5.34) to the present (1.7.0):

  • 2 releases (1.6.0 on August 6, 1.7.0 on August 18). 227 commits between 1.5.34 and 1.7.0
  • The official supported services count stays at 69 in the 1.7.0 tag README. As of August 28 on main, it is 82
  • GitHub stars went from 18,022 → 22,504
  • The README compatibility test count remains at 2,506
  • AppSync's GraphQL endpoint now works. However, resolvers do not work
  • CloudFront now delivers content. Signed URLs are also validated
  • IAM can now resolve 1,566 AWS managed policies. However, all of them are effectively Allow *
  • Cross-account storage isolation was fixed
  • New services include MWAA (real Airflow), CloudWatch RUM, SWF, Managed Service for Apache Flink, and others

Let's go through them in order.

Numbers differ between the release version and main

After 1.5.34 (July 29) came 1.6.0 (August 6), then 1.7.0 (August 18), and there have been no releases since as of August 28. While we haven't returned to the 3–4 day intervals of late June through early July, development has not stopped, and 160 commits have been added to main after 1.7.0. Organizations, Resource Explorer 2, FIS, EFS, and others are included in this unreleased portion. I'll run 1.6.0 and 1.7.0 locally, as those are the released versions.

The supported service count in the README is where this discrepancy is visible. The README at the 1.7.0 tag still says "69 AWS services" as before, and while rows for SWF and MWAA were added to the service list in the same tag, the total count didn't follow. The number only moved after the release.

Date README count Trigger
August 18 (1.7.0) 69 Unchanged from previous
August 21 72 Updated to coincide with FIS addition (#2435)
August 23 73 "Add MWAA to table, correct service count" (#2496)
August 24 76 Added check to detect services missing from the list (#2465)
August 27 82 Added Network Firewall, Route 53 Resolver, and others

The difference from 69 to 82 is a mix of corrections and unreleased new services. Since #2465 introduced a check to cross-reference code with the list, missing list updates should be easier to catch going forward.

There is also some variability in how "new services" are counted in release notes. Bedrock, included in the "3" for 1.6.0, was already in the list before; what was added this time is a proxy that relays to the OpenAI-compatible API. Transit Gateway, included in the "9" for 1.7.0, is a feature within EC2. The numbers cannot simply be added up.

The compatibility test count in the README table was 2,506 in all of 1.5.34, 1.7.0, and main, with the same breakdown. There are changes in the compatibility-tests directory, so while the table number has stayed the same, it can't be said definitively that tests haven't increased.

Homework from last time

The environment setup is the same as before — prepare a docker-compose.yml and start it up.

services:
  floci:
    image: floci/floci:1.7.0
    ports:
      - "4566:4566"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
$ export AWS_ENDPOINT_URL=http://localhost:4566
$ export AWS_DEFAULT_REGION=us-east-1
$ export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test

AppSync: GraphQL endpoint works, but resolvers do not

Last time, POSTing to the GraphQL endpoint returned an S3 XML error. The "Phase 6: Query Execution + HTTP Endpoint" PR (#1884) that was open at the time was included in 1.7.0. The release notes say "a working GraphQL service that executes queries against real data sources."

What I want to verify is "whether resolvers work and return values from data sources." I created an API with a resolver on a NONE data source that returns a fixed message, and a resolver on a DynamoDB data source for GetItem, and sent queries. I put an item in the DynamoDB table and also created an IAM role for the data source.

$ curl -s "http://localhost:4566/v1/apis/${API_ID}/graphql" \
    -H "Content-Type: application/json" -H "x-api-key: ${API_KEY}" \
    --data '{"query":"query { hello(name: \"Floci\") { message } }"}'
{"data":{"hello":null}}

$ curl -s "http://localhost:4566/v1/apis/${API_ID}/graphql" \
    -H "Content-Type: application/json" -H "x-api-key: ${API_KEY}" \
    --data '{"query":"query { getOrder(pk: \"o-1\") { pk item } }"}'
{"data":{"getOrder":null}}

The S3 error from last time is resolved, and GraphQL responses are now returned. However, the value is null in both cases, and the resolvers are not executing. Syntax errors and undefined fields appear in errors, and introspection also works, so the endpoint and query engine are usable.

This is as documented. The docs/services/appsync.md at the 1.7.0 tag states "Nullable fields may be null until DataFetchers (Phase 8)." Resolver dispatch is Phase 8, and data source connections such as DynamoDB are Phase 9. The release notes say "real data sources," but what's usable in 1.7.0 is the scope of Phase 6 — that is, the HTTP endpoint and query interpretation only.

Authentication (Phase 7) was merged on August 19 but is not yet released. In 1.7.0, a 200 is returned even without x-api-key. Testing with the nightly image (floci/floci:nightly-08272026) showed that requests without a key return 401. However, even providing the id obtained from create-api-key resulted in 401. Looking at the raw response, there is a field called apiKey separate from id, and it is this field that is matched during authentication.

{"apiKeys":[{"id":"e2f514f","apiId":"...","apiKey":"da2-cc88835"}],"nextToken":null}

AWS's ApiKey has no apiKey field — the id itself is the key value starting with da2-. Since CLIs and SDKs discard fields not in the API model, API key authentication via the SDK won't work as-is. I submitted a PR to use id as the key value (#2645), and it was merged the same day.

#2042: >= in Logs Insights changed to "ignored with a warning"

Last time, I filed an issue about Logs Insights filter silently ignoring unsupported syntax. The issue was that like returned all records while >= returned 0 records.

In 1.7.0, >= now returns all records, and the server log outputs a warning like Ignoring unsupported Logs Insights filter: @message >= "x". This brings it in line with how like and stats are handled. A PR by someone else (#2170), referencing the issue, fixed the misparse of >=, <=, and =~.

The core question of the issue — "should unsupported syntax result in an error?" — was left as "defer to the maintainer's judgment" within the PR, and the issue remains open.

#2043: Cloud Control can create but not list

The behavior of returning an empty list for unsupported types hasn't changed in 1.7.0. The supported types for ListResources increased from 6 to 9 (#2062, merged into #2037).

On the other hand, 1.7.0 added CreateResource, DeleteResource, and GetResource (#2037), reusing the CloudFormation provisioner. Testing with an SQS queue, creation and individual retrieval work, but listing returns empty.

$ aws cloudcontrol create-resource --type-name AWS::SQS::Queue --desired-state '{"QueueName":"cc-created"}'
# → GetResourceRequestStatus returns SUCCESS
$ aws cloudcontrol get-resource --type-name AWS::SQS::Queue \
    --identifier http://localhost:4566/000000000000/cc-created \
    --query 'ResourceDescription.Properties' --output text
{"QueueName":"cc-created","Id":"http://localhost:4566/000000000000/cc-created"}
$ aws cloudcontrol list-resources --type-name AWS::SQS::Queue --query 'ResourceDescriptions'
[]

There is a gap between APIs: creation and individual retrieval broadly support types that CloudFormation can handle, while listing only supports 9 types. Last time it was a matter of "unsupported types," but now even the same type yields inconsistent results between creation and listing.

What I found by running 1.7.0

CloudFront now delivers content

Until now, CloudFront only stored configuration such as distributions. In 1.7.0, delivery from S3 origins and custom origins (#1820) and signed URL validation (#1831) have been added.

I placed index.html in an S3 bucket, created a distribution with it as the origin, and sent a request directly to Floci's port with the distribution's domain name in the Host header.

$ curl -s -i -H "Host: EE0UZT5JDQPAM1.cloudfront.net" http://localhost:4566/
HTTP/1.1 200 OK
Content-Type: text/html
...

<h1>hello from S3 origin</h1>

The S3 object was returned. DefaultRootObject also works, returning index.html for requests to /. Non-existent keys return 404.

For signed URLs, I registered a public key from an RSA key pair and a key group, and tested with a behavior that has TrustedKeyGroups enabled.

Access Result
No signature 403
Signed URL created with aws cloudfront sign 200
Expired signed URL 403

It's now possible to locally test a setup where S3 objects can only be retrieved via signed URLs.

There are caveats regarding scope. According to the documentation, data-plane evaluation of cache policies and origin request policies is not yet implemented, and viewer query strings are not forwarded to the origin. Response headers are also returned as-is from S3. It's best to think of what works as "routing to origin and signature validation."

Cross-account storage isolation

Floci treats a 12-digit number used as an access key as an account ID. The 1.7.0 release notes include a fix for isolating DynamoDB items (#2240) and S3 object bodies (#2241) between accounts. The fact that a fix was needed implies they were not isolated before. I compared the same operations between 1.5.34 and 1.7.0.

I created an orders table with account 111111111111 (A) and put one item in it, then created a table with the same name using account 222222222222 (B). I did the same with S3, writing to the same key in the same bucket name from B.

Operation 1.5.34 1.7.0
A's scan after B creates a table with the same name 0 items (A's item disappears) 1 item
A's get-object after B does put-object to the same bucket/key Returns B's content Returns A's content
A's get-object after B does delete-object on that key InternalError Returns A's content

In 1.5.34, although table and bucket listings were separated per account, the contents were shared by name. In 1.7.0, A's content is preserved.

Since S3 bucket names are globally unique in actual AWS, the S3 row is a test to verify Floci's internal storage isolation. For AWS-compatibility verification, refer to the DynamoDB row, where resources with the same name can be created per account just as in actual AWS. The verification I could confirm is limited to these two services.

IAM managed policies: resolving 1,566 entries and permission evaluation are different things

In 1.7.0, IAM now has the full catalog of AWS managed policies (#2194). Previously, only 59 hand-written entries were included, and any other arn:aws:iam::aws:policy/... would return NoSuchEntity. This addresses the issue of CDK or Terraform failing when referencing real managed policies.

$ aws iam list-policies --scope AWS --query 'length(Policies)'
1566

Real ARNs can be attached, and nonexistent ARNs are rejected. In terms of name resolution, "full catalog" is accurate. However, the policy content is a different matter.

$ aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
    --version-id v1 --query 'PolicyVersion.Document.Statement'
[{"Effect": "Allow", "Action": "*", "Resource": "*"}]

This is an allow-all document. The source managed-policies.yaml also explicitly states "policy documents are not modeled." Since Floci does not evaluate policies by default, the design is to resolve all entries to the same allow-all document.

This limitation becomes apparent when IAM enforcement mode (FLOCI_SERVICES_IAM_ENFORCEMENT_ENABLED=true, disabled by default), covered in the previous article, is enabled. Let's try operating as a user with only AmazonS3ReadOnlyAccess attached.

# Run with the access key of ro-user (AmazonS3ReadOnlyAccess only)
$ aws s3 mb s3://ro-made-this
make_bucket: ro-made-this
$ echo written-by-ro | aws s3 cp - s3://root-bucket/y.txt
$ aws sqs create-queue --queue-name ro-q
{
    "QueueUrl": "http://localhost:4566/000000000000/ro-q"
}

A supposedly read-only user succeeded in creating a bucket, writing an object, and creating an SQS queue. With a user whose read permissions were written as an inline policy using s3:Get* and s3:List*, CreateBucket and PutObject return AccessDenied. Inline policies are evaluated as written, while managed policies are evaluated using the Allow * that Floci holds.

For the purpose of running IaC, the catalog of 1,566 entries is useful. However, it cannot be used for the purpose of locally verifying "whether the managed policies attached to this role are sufficient." It would be good to have a note about this in the enforcement mode documentation.

Other updates

I haven't verified these locally, but here are notable changes listed in the release notes.

Category Content Version
Lambda Implementation of Lambda Extensions API (#1773) 1.6.0
MWAA Added MWAA with a real Airflow (LocalExecutor) as the backend (#2086) 1.6.0
Bedrock Proxy backend that relays Converse to the OpenAI-compatible API (#1789). InvokeModel remains a stub, streaming returns 501 1.6.0
Cognito Refresh tokens are HMAC-signed, and expiration and pool scope are validated (#2132, #2135, #2139) 1.7.0
EKS Support for IRSA, including OIDC issuer and JWT validation (#2108) 1.7.0
CloudFormation Added 11 types including AWS::EC2::VPCEndpoint and network ACLs. Per-resource DeletionPolicy and Condition, and GetTemplateSummary 1.7.0
Firehose Time- and size-based flushing with BufferingHints (#2314) 1.7.0
Distribution Mechanism to cut a release from main with one button, and versioned publishing to ECR Public (#2127) 1.6.0

Summary

Over the past month, Floci saw two releases, but those two releases contained 227 commits and incremented the minor version twice. Here is what I found by running things locally:

  • AppSync's GraphQL HTTP endpoint now works, and the S3 error from last time is resolved. However, resolvers are not yet implemented — configured resolvers do not execute and return null. This is per the documentation, awaiting Phase 8 and beyond
  • The misparse of >= in Logs Insights was fixed to "ignored with a warning" by another person's PR. The core question of whether unsupported syntax should be an error remains open
  • Cloud Control now has CreateResource and GetResource, so an SQS queue can be created and read, but ListResources for the same type remains empty
  • CloudFront's delivery from S3 origins and signed URL validation now work. Cache policy evaluation is not yet implemented
  • The content collision that occurred between same-named tables and keys in different accounts has been resolved in 1.7.0
  • IAM managed policies can resolve 1,566 names, but all of their contents are effectively Allow *. In enforcement mode, a user with a read-only policy can still write

The supported service count in the README stays at 69 for the 1.7.0 tag but is 82 on main. The "new services" count in release notes, the README numbers, and the state of main all move at different times, so it's necessary to align which point in time you're reading the numbers from.

Among the things I noticed, I was able to trace the AppSync API key issue down to the root cause and submitted a PR (#2645). The fix makes CreateApiKey return the id in the same da2- format as AWS, and authentication matches against that id. It was merged the same day I submitted it, and like Phase 7, it's waiting for the next release. The 160 commits accumulated in main after 1.7.0 are planned to be reviewed again once they make it into an official release.

The figures and release content in this article are based on the following primary sources:

Share this article

AWSのお困り事はクラスメソッドへ

Related articles